O CAP não se aplica aqui
O teorema CAP trata de sistemas distribuídos. Esta aplicação não é um, e entender por quê já é boa parte de entender o teorema.
Um nó
Do curso de nível Sênior "Arquitetura: banco de dados multi-região": o config/deploy.yml cita um único NOR_SERVER_IP, e tanto a aplicação Rails quanto o contêiner PostgreSQL rodam nele.
Um nó sozinho não pode ser particionado de si mesmo. Não há rede entre o banco e ele próprio, então não há mensagens para perder, e a condição de que o teorema trata não existe.
Isso não é uma brecha. O teorema descreve o que acontece quando um estado replicado fica separado por uma rede, e esta aplicação não tem estado replicado em lugar nenhum.
Do que ela depende no lugar disso
Ela não depende de consistência eventual nem de quóruns. Depende da linearizabilidade de nó único, que o PostgreSQL oferece. É a mesma propriedade que o CAP chama de consistência, e num nó só ela sai de graça.
# app/services/enrollment/award_badges_service.rb
# Unique index makes this safe under concurrency; only count fresh rows.
user_badge = ::Enrollment::UserBadge.create_or_find_by!(user: user, badge: badge)
badge if user_badge.previously_new_record?
Esse comentário é um argumento de consistência, escrito. Duas requisições concorrentes verificam se um badge existe, as duas não encontram nada e as duas inserem. O índice único em [user_id, badge_id] faz uma inserção dar certo e a outra levantar erro. O create_or_find_by! trata o erro, e o previously_new_record? diz quem venceu.
Não há trava nem coordenação: a garantia de unicidade do banco faz o trabalho. O Enrollment::UserBadge também declara validates :badge_id, uniqueness: { scope: :user_id }, mas não é essa validação Ruby que torna isso seguro. As duas requisições concorrentes passam por ela, porque cada uma consulta antes de qualquer uma inserir.
A validação Ruby dá uma mensagem de erro amigável, e o índice dá a garantia. É essa a distinção, e a garantia é o que deixa de sair de graça no momento em que existem dois nós de banco.
As garantias de unicidade que esta aplicação mantém
Em 28/09/2026, as tabelas da própria aplicação tinham vinte índices únicos. Estes são os que o produto usa de verdade:
bookmarks [user_id, lesson_id]
enrollments [user_id, course_id]
lesson_completions [user_id, lesson_id]
lesson_notes [user_id, lesson_id]
user_badges [user_id, badge_id]
experience_points [user_id, idempotency_key]
identity_authentications [provider, uid]
partner_course_sponsorships [partner_id, course_id]
users [email], [username]
courses [slug]
lessons [slug]
levels [xp_required]
Quase todos têm um validates ... uniqueness correspondente no model, e cada uma dessas validações tem uma condição de corrida. Ela consulta, não acha nada e diz que está válido, enquanto outra requisição faz o mesmo.
A checagem em Ruby serve para a mensagem, e o índice serve para a garantia. O índice é uma propriedade de um único nó PostgreSQL.
flowchart TB
A["um nó PostgreSQL"] --> B["nenhuma rede entre ele e ele mesmo"]
B --> C["nenhuma mensagem a descartar"]
C --> D["nenhuma partição. o CAP não tem o que dizer"]
A --> E["linearizabilidade de nó único"]
E --> F["20 índices únicos carregando a garantia real"]
F --> G["as validações Ruby correm; o índice não"]
G --> H["exatamente o que deixa de ser grátis com dois nós"]
Quando o CAP começa a valer
O CAP passa a valer no momento em que dois nós guardam o mesmo estado com uma rede entre eles.
Uma réplica de leitura, o passo 4 do curso anterior. Agora há um primário, uma réplica, uma rede e uma pergunta sobre o que uma leitura devolve quando a rede cai.
Dois servidores de aplicação com caches locais. O Solid Cache aqui é uma tabela de banco, então o cache de uma região não é o da outra. Um valor em cache que está certo numa região e velho na outra é estado replicado com uma rede no meio, seja qual for o nome que se dê a isso.
Uma fila consumida de dois lugares. Os workers do Solid Queue aqui rodam todos na única máquina. Duas regiões consumindo a mesma fila precisam de uma trava distribuída, que é um problema de CAP em roupa de trabalho.
Acrescente qualquer uma das três, e todas as perguntas deste curso passam a valer. Nenhuma delas é verdade hoje.
Por que aprender um teorema que não se aplica
São dois motivos, e o segundo é o que importa.
Ele mostra do que você abriria mão. As restrições de unicidade acima saem de graça hoje, e são a primeira baixa de um segundo nó. Saber disso é o que permitiu ao curso de multi-região dizer que "o caminho de concessão de XP precisa de consistência forte", em vez de descobrir isso depois.
Ele é também o resultado mais mal citado da área. Quase tudo o que as pessoas acreditam sobre ele está errado de um jeito que leva a arquitetura ruim: "somos um sistema AP", "escolha dois de três", "você precisa escolher". A lição 3 trata disso, e a correção importa mais que o próprio teorema.
O teorema cabe numa página, e as leituras erradas dele fizeram mais estrago que a restrição que ele descreve.
Pontos-chave
- Um nó PostgreSQL não pode ser particionado de si mesmo, então o CAP descreve uma condição que esta aplicação não tem.
- A aplicação depende da linearizabilidade de nó único: vinte índices únicos carregam garantias que as validações Ruby correspondentes não conseguem dar.
- O
create_or_find_by!com um índice único é um argumento de consistência, escrito num comentário. - O CAP começa a valer com uma réplica de leitura, caches por região ou uma fila consumida de dois lugares.
- O teorema cabe numa página, e as leituras erradas dele fizeram mais estrago que a restrição que ele descreve.
Continue com a aula 2
O teorema de fato
Definições de consistência, disponibilidade e tolerância a partição, a prova com dois nós e por que P não é escolha.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.