PostgreSQL, SQL, MongoDB e DynamoDB — conceitos essenciais explicados a fundo.
MongoDB Search e MongoDB Vector Search (antes Atlas Search / Atlas Vector Search) são busca full-text e busca semântica ANN/HNSW apoiadas em Lucene, servidas por um processo mongot separado e sincronizado via change streams. São eventualmente consistentes, limitadas pela memória ocupada pelo índice vetorial e, desde o MongoDB 8.2, disponíveis em implantações autogerenciadas e não só no Atlas, embora com um conjunto de recursos materialmente mais restrito que a plataforma gerenciada.
Um pipeline de agregação é uma cadeia de estágios no estilo bash sobre um fluxo de documentos, e a habilidade está em saber quais estágios encolhem o fluxo ($match, $group), quais o fazem crescer ($unwind) e quais só o remodelam ($project, $sort), e então ordená-los para que expressões de caminho de campo, acumuladores e um _id de $group bem rotulado produzam o número certo, e não um número plausível.
Consultar o MongoDB com find (projeções, condicionais $, $in/$or/$not e as armadilhas específicas de tipo em torno de null, arrays e documentos embutidos) e então conduzir o cursor resultante com limit, skip e sort sem cair na paginação com skip grande.
Como o MERGE atualiza, apaga ou insere linhas condicionalmente em uma única instrução atômica, conforme as linhas de origem casem ou não, por que o PostgreSQL não tinha MERGE nenhum até a versão 15 (2022), dois anos depois da 2ª edição deste livro, e por que o INSERT ... ON DUPLICATE KEY UPDATE do MySQL consegue inserir ou atualizar, mas nunca apagar, na mesma instrução.
Como UNION ALL empilha conjuntos de linhas verticalmente e UNION faz o mesmo deduplicando (ao custo de uma ordenação), por que as regras de quantidade e compatibilidade de tipos das colunas são idênticas no PostgreSQL, MySQL e SQL Server, e como o MySQL 8.0.31 (2022) fechou sua antiga lacuna de INTERSECT/EXCEPT, enquanto o SQL Server ainda não tem a variante ALL para nenhum dos dois.
Como ordenar NULLs para uma posição específica independentemente da ordem dos valores não NULL, e como ordenar por uma chave que depende do valor de outra coluna; e por que a afirmação do livro de que o PostgreSQL precisa do mesmo workaround que MySQL/SQL Server para ordenação de NULL é simplesmente errada (o PostgreSQL tem NULLS FIRST/LAST nativo desde 2008).
Como IS NULL/IS NOT NULL e COALESCE tratam dados ausentes de forma portável entre bancos, como os curingas % e _ do LIKE casam texto parcial, e as pegadinhas de sensibilidade a maiúsculas e de índices (curingas no início vs no fim, pg_trgm) que diferem entre PostgreSQL, MySQL e SQL Server.
Como o PostgreSQL, o MySQL e o SQL Server limitam o número de linhas que uma consulta retorna (LIMIT, TOP e o padrão ANSI FETCH FIRST, que o MySQL não suporta), como embaralhar a amostra antes de limitá-la e por que TABLESAMPLE é melhor que ORDER BY RANDOM() para tabelas grandes.
Removendo linhas identificadas por seu relacionamento com uma segunda tabela: a forma de subconsulta IN/EXISTS, a armadilha de NULL do NOT IN no caso inverso, e as três sintaxes de DELETE com join incompatíveis entre fornecedores.
Como manter arbitrariamente uma linha por grupo de duplicata e apagar o resto com MIN(id) NOT IN, por que o MySQL ainda rejeita uma subconsulta de DELETE que lê sua própria tabela alvo, e como ROW_NUMBER() sobre uma CTE oferece uma alternativa mais portável, embora não com sintaxe idêntica, entre PostgreSQL, MySQL, e SQL Server.
Como retornar linhas sem correspondência dos dois lados de um join ao mesmo tempo (departamentos sem funcionários e funcionários sem departamento, simultaneamente) usando FULL OUTER JOIN onde é suportado, ou um UNION de um outer join LEFT e RIGHT onde não é (MySQL, e a sintaxe proprietária (+) do Oracle).
Como somar corretamente uma coluna em todas as linhas de uma tabela quando uma segunda tabela, unida por join, só tem linhas correspondentes para algumas delas, usando outer join em vez de inner join e deixando a expressão CASE tratar uma correspondência NULL como zero em vez de pular a linha.
Por que fazer o join antes de agregar pode inflar em silêncio um SUM ao contar mais de uma vez uma linha duplicada, e como corrigir isso com SUM(DISTINCT ...), pré-agregando em uma subconsulta antes do join, ou com uma window function no DB2/Oracle/SQL Server.
Como uma condição de join ausente ou incompleta multiplica silenciosamente a contagem de linhas em vez de dar erro, por que a contagem de linhas resultante é sempre o produto das cardinalidades de entrada, e por que a sintaxe explícita ANSI JOIN pega o erro em tempo de parse onde joins antigos por vírgula deixam rodar.
Use uma diferença de conjunto simétrica (EXCEPT/MINUS, ou um fallback de subconsulta correlacionada) para provar que duas tabelas ou views guardam exatamente as mesmas linhas, duplicatas inclusas.
Como LEFT JOIN + IS NULL (um anti-join) encontra linhas em uma tabela sem correspondência em outra, como o mesmo mecanismo de LEFT JOIN (ou uma subconsulta escalar na lista SELECT) adiciona dados opcionais a uma consulta já correta sem descartar linhas, e por que tanto MySQL quanto PostgreSQL reconhecem o padrão de anti-join e param de varrer cedo em vez de avaliá-lo como um join-depois-filtro ingênuo.
Use LEAD e LAG para ler ou deslocar o valor de uma linha vizinha, e RANK, DENSE_RANK e ROW_NUMBER para atribuir posições ordinais com três regras diferentes de tratamento de empates.
Produzir linhas de subtotal e de total geral junto com as linhas de detalhe em uma única consulta (ROLLUP para uma hierarquia, CUBE ou GROUPING SETS para toda combinação de colunas de agrupamento) e usar GROUPING() para distinguir um NULL de subtotal fabricado de um NULL real, com o MySQL Server ainda limitado apenas ao ROLLUP.
Dividindo um conjunto de resultado em buckets de tamanho fixo com ROW_NUMBER e CEILING versus um número fixo de buckets com NTILE, depois renderizando a distribuição como um histograma de texto horizontal ou vertical.
Toda uma família de problemas do tipo "quais linhas, e quantas" que vai além de um LIMIT simples, como paginar resultados, amostrar uma linha a cada N, lidar com empates em uma consulta top-N e encontrar o maior/menor valor por grupo.
Compare uma linha com sua vizinha dentro de uma partição ordenada com LEAD/LAG, e carregue o último valor conhecido adiante através de lacunas NULL com LAST_VALUE ou um FIRST_VALUE de grupo contado; mais as lacunas de portabilidade reais em expressões de offset e IGNORE NULLS.
Encontrar sequências contíguas em uma sequência numérica esparsa, reduzir cada sequência ao seu início e fim, e gerar a sequência densa que revela o que está faltando.
Calcular a moda, a mediana e um score de outlier por desvio absoluto mediano em SQL, e por que o PostgreSQL, o MySQL e o SQL Server precisam cada um de uma consulta diferente para isso.
Uma única forma de window function, SUM/AVG OVER com um ORDER BY e um frame, resolve totais acumulados, produtos acumulados, médias móveis e acumuladores que reiniciam, incluindo por que um produto acumulado não tem agregação nativa e por que o frame RANGE padrão é uma armadilha relacionada a empates.
Calcular várias agregações diferentes em nível de partição lado a lado em uma única consulta, e janelas móveis delimitadas de forma simétrica ou por intervalo de valores, em vez de por uma contagem de linhas para trás.
Raciocine sobre datas como intervalos em vez de pontos: meça a lacuna para a próxima linha com LEAD, preencha buracos em uma sequência de data com um calendário gerado, e detecte faixas de data colidindo com uma condição de self-join ou os tipos de faixa nativos do PostgreSQL.
Encontrar todas as ocorrências de um dia da semana em um ano e a primeira/última ocorrência em um mês, navegando pelas convenções incompatíveis de numeração dos dias da semana no PostgreSQL, no MySQL e no SQL Server.
Fabricar linhas de data que nenhuma tabela guarda (uma grade completa de calendário, ou as datas de início e fim de um trimestre fiscal) com generate_series, CTEs recursivas e o GENERATE_SERIES apenas numérico do SQL Server 2022.
Decompondo uma data em status de ano bissexto, dias no ano, unidades individuais de tempo, e limites de mês; e onde EOMONTH, LAST_DAY, e DATETRUNC substituíram a aritmética manual.
Contando dias úteis entre duas datas e somando ocorrências de dia da semana ao longo de um ano; gerando uma linha por dia com generate_series, CTEs recursivas, ou uma tabela de números, e evitando a numeração de dia da semana dependente de sessão @@DATEFIRST do SQL Server.
Adicionando intervalos a datas e medindo a distância entre duas datas em dias, meses, anos, ou unidades menores que um dia; e por que o SQL Server conta fronteiras de calendário onde PostgreSQL e MySQL truncam unidades decorridas.
Limpar caracteres indesejados de uma string, separar dados alfanuméricos misturados em suas partes numérica e de caracteres, e provar que um valor de texto pode ser convertido, comparando o truque TRANSLATE/REPLACE do livro com as expressões regulares e os validadores orientados a tipo de hoje.
Contar quantas vezes um caractere ou substring aparece em um valor e encontrar linhas que não seguem um padrão esperado, com as técnicas portáveis de LENGTH/REPLACE e TRANSLATE e as funções nativas de regex que cada fornecedor adicionou desde então.
Colapsando linhas em uma string separada por vírgula com STRING_AGG/GROUP_CONCAT, dividindo uma string delimitada de volta em linhas para uma lista IN ou join, e extraindo o n-ésimo token; com as diferenças por fornecedor que ainda importam.
Float e double não conseguem representar a maioria das frações decimais exatamente porque são base 2, então 0.1 + 0.2 !== 0.3 e totais monetários derivam silenciosamente; o conserto é NUMERIC/DECIMAL, unidades menores inteiras, ou BigDecimal, não arredondar mais forte a cada passo.
Expressar uma hierarquia pai-filho autorreferenciada com uma cadeia fixa de self-joins (um join por nível) e reconhecer onde a cadeia deixa de escalar e uma CTE recursiva assume.
Percorrer uma tabela autorreferenciada até uma profundidade arbitrária com uma CTE recursiva (visões completas da árvore, todos os descendentes de um nó e classificação em folha/ramo/raiz), além dos limites de profundidade de recursão de cada fornecedor, que decidem quando ela quebra.
Remodelar linhas agrupadas em colunas com a técnica portável de CASE dentro de uma agregação, e ver onde o operador PIVOT do SQL Server e o crosstab() do PostgreSQL realmente ajudam.
Colapsar várias colunas do mesmo tipo em uma coluna de valor mais uma coluna de rótulo, o inverso de pivotar, usando UNION ALL, uma lista LATERAL + VALUES de leitura única, ou o operador nativo UNPIVOT do SQL Server.
Cobre o modelo de replicação multi-master do CouchDB (sem primário designado, qualquer nó aceita escritas diretamente, com sincronização bidirecional real construída a partir de dois jobs de replicação unidirecionais), replicação ad hoc versus contínua e o banco _replicator para jobs duráveis, como conflitos de replicação são detectados e expostos via um algoritmo determinístico de vencedor e o campo _conflicts, e os tipos de feed polling/long-polling/contínuo/eventsource da Changes API com filter functions e filtragem por _selector, além de uma checagem book-vs-today confirmando a maturidade do clustering do CouchDB 3.x e o status ativo do PouchDB como Apache PouchDB.
O CouchDB não tem linguagem de query ad-hoc para nada além de buscas simples por chave, toda query não trivial é uma view map/reduce predefinida, escrita em JavaScript com emit(), salva em um design document, e materializada incrementalmente em um índice B-tree conforme documentos mudam. Desde o CouchDB 2.0 (2016), as queries declarativas _find do Mango cobrem buscas simples sem JavaScript escrito à mão, mas elas mesmas são construídas sobre a mesma infraestrutura de views MapReduce e ainda não conseguem substituir views para padrões de agregação ou fan-out.
O CouchDB expõe documentos exclusivamente sobre HTTP/REST, sem protocolo de fio, sem driver binário, toda operação tem a forma de uma requisição curl, e substitui locking por MVCC baseado em revisão, um _rev desatualizado recebe 409 em um único nó, mas entre réplicas ambas as escritas concorrentes têm sucesso e o conflito resultante é detectado e mesclado pela aplicação, nunca pelo banco de dados.
Uma coleção de séries temporais é um tipo de coleção do MongoDB 5.0 que transforma um documento por leitura em buckets colunares comprimidos, indexados por um metaField imutável mais uma janela de tempo arredondada. Ela traz ganhos reais de armazenamento e de consulta para dados de medição append-only ao custo de uma lista longa e específica de restrições: updates só no metaField, sem índices únicos ou de texto, sem change streams, sem escritas em transações e uma escolha de metaField que não pode ser mudada depois.
O design de schema no MongoDB começa pelas consultas em vez das entidades, então o método do capítulo é estabelecer primeiro restrições, padrões de acesso, tipos de relação e cardinalidade, depois recorrer a um padrão nomeado (Bucket, Subset, Outlier, Extended Reference) e decidir campo a campo entre embutir ou referenciar, com uma seção final honesta sobre os joins que o MongoDB nunca foi projetado para fazer.
O caminho de escrita do MongoDB é insertOne/insertMany, deleteOne/deleteMany/drop, e updateOne/updateMany/replaceOne, e as decisões que de fato mordem são substituição versus operadores $, batches ordenados versus não ordenados, e se upsert silenciosamente transforma uma correspondência falha em um documento totalmente novo.
O MongoDB troca linhas e tabelas por documentos e collections com schema dinâmico, usando documentos embutidos e arrays onde um modelo relacional faria JOIN, e um ObjectId de 12 bytes (timestamp de 4 bytes, 5 bytes aleatórios, contador de 3 bytes) para cunhar valores _id únicos sem coordenação entre servidores.
Além da B-tree de uso geral: índices geoespaciais 2dsphere para consultas de localização, índices de texto para busca básica por palavras-chave (hoje explicitamente substituídos pelo MongoDB Search em produção), índices TTL para expiração automática, capped collections para filas de tamanho fixo ordenadas por inserção e GridFS para arquivos acima do limite de 16 MB por documento. Cada um tem armadilhas próprias que o livro aponta e que a documentação atual deixa ainda mais claras.
A shard key decide em qual shard cada documento vai parar, e as três formas de distribuição do capítulo explicam as consequências. Uma chave ascendente afunila todo insert em um único max chunk, uma chave hashed espalha as escritas por igual ao particionar por intervalos um espaço de hash de 64 bits, mas nunca consegue atender uma consulta por intervalo direcionada, e uma chave baseada em localização mais zonas compra controle de posicionamento ao custo da ajuda do balancer, com a cardinalidade limitando até onde qualquer uma delas pode ser dividida.
Todo índice do MongoDB é uma B-tree real do WiredTiger em disco, e é por isso que um índice composto atende qualquer prefixo de suas chaves e nada mais, que a ordem de chaves igualdade-ordenação-intervalo leva a consulta de exemplo do livro de 4.325 ms para 37 ms, e que todo índice que você adiciona é um imposto sobre toda escrita.
Como o serviço de indexação separado do Neo4j (índices de nó chave-valor e full-text/Lucene) e suas primitivas nativas de busca de caminhos tornam buscas e consultas de caminho mais curto recursos de primeira classe, e não código do lado do cliente, percorrido pelo exercício shortestPath() de Kevin Bacon do livro, por um rastro de Dijkstra em grafo ponderado feito do zero, e por como tanto a antiga API REST de indexação quanto a opção única dijkstra deram lugar ao CREATE INDEX/CREATE FULLTEXT INDEX apoiado no schema e à biblioteca muito maior de algoritmos Graph Data Science.
Como o modelo de grafo de propriedades do Neo4j (nós com labels e propriedades, relacionamentos com tipo, direção e propriedades próprias) difere fundamentalmente dos modelos relacional e de documentos, por que ele é "amigo do quadro branco", e o vocabulário central de CRUD do Cypher (CREATE, MATCH, WHERE, RETURN, MERGE, DELETE/DETACH DELETE) para construir e consultar um grafo.
Como um cluster HBase de fato mantém uma tabela coesa por baixo da API, regions como intervalos de row-key contíguos e sem sobreposição servidos por RegionServers, HMaster tratando atribuição de region e failover (não o próprio caminho de leitura/escrita), HDFS como a camada de armazenamento durável através da qual todo RegionServer escreve StoreFiles e WALs, ZooKeeper coordenando eleição de master e a localização de bootstrap do hbase:meta, e o próprio hbase:meta como uma tabela comum, divisível, que mapeia row keys para regions e servidores donos, além de como o MapReduce paraleliza jobs em lote uma map task por region via TableInputFormat/TableOutputFormat, com notas book-vs-today sobre políticas de split sensíveis a tamanho, a ascensão do Spark-on-HBase para novo trabalho de analytics, e confirmação de que o HBase continua sendo lançado e mantido ativamente.
O mapa multidimensional esparso, distribuído, ordenado do HBase (row key, column family, column qualifier, timestamp/versão para um valor de célula), CRUD e administração de tabela através do shell do HBase (create, put, get, scan, disable, alter, enable), e uma comparação explícita com o modelo wide-column do Cassandra, mesma linhagem do paper Bigtable e vocabulário de column-family, mas escritas coordenadas por master sobre HDFS e ZooKeeper com consistência forte no nível de linha, versus a arquitetura sem líder, de consistência ajustável, do Cassandra, além de uma checagem book-vs-today sobre o status de manutenção atual do HBase e a posição de mercado em mudança contra Cassandra/ScyllaDB e Bigtable/DynamoDB gerenciados.
O livro documenta o modelo HA master-slave do Neo4j, hoje removido, em que slaves aceitavam escritas e sincronizavam de volta com um master eleito; conferido com a documentação atual do Neo4j, este conceito percorre o que o substituiu, o Causal Clustering baseado em Raft com servidores core e read replicas, depois renomeados para papéis primary/secondary por banco no Neo4j 5, além das mudanças de roteamento, bookmarks e licenciamento que vieram junto.
Como o driver de uma aplicação realmente se conecta a um replica set e o usa, passando por seed lists e connection strings de DNS seedlist, descoberta do primário e comportamento de failover do lado do driver, write concern (w: 1, w: majority, regras customizadas getLastErrorModes) como a ferramenta que impede ver um falso sucesso em uma escrita prestes a ser revertida, e os cinco modos de read preference que trocam consistência por disponibilidade ou latência.
Como um replica set do MongoDB realmente faz failover, com o modelo de primário/secundário/oplog, eleições baseadas em maioria e desenho de quórum entre data centers, a mecânica de rollback depois de um failover e os ajustes de priority/hidden/votes/arbiter usados para definir qual membro vira primário.
Um pequeno adendo sem fonte no livro, verificado ao vivo contra a documentação atual da AWS, quatro mudanças genuinamente aditivas do DynamoDB desde 2020, não cobertas em outro lugar nesta categoria, a classe de tabela Standard-IA otimizada por custo (dez. 2021), políticas baseadas em recurso para acesso entre contas simplificado (mar. 2024), integrações zero-ETL para Redshift e OpenSearch (2023-2024), e Multi-Region Strong Consistency opt-in para Global Tables (GA junho 2025), cada uma com suas restrições mecânicas reais, não só seu enquadramento de marketing.
O caso de orientação para o DynamoDB antes de aprofundar, um armazenamento key-value e wide-column totalmente gerenciado, construído para escalonamento infinito sem degradação de performance, um modelo de conexão HTTP/IAM que se encaixa em computação serverless, e precificação baseada em carga de trabalho, motivado historicamente por hiperescala (o paper Dynamo de 2007) e computação hiper-efêmera (Lambda), contrastado honestamente contra bancos de dados relacionais, MongoDB, e Cassandra, incluindo os trade-offs que DeBrie não suaviza, sem joins nunca, aplicação de schema movida para sua aplicação, e lock-in permanente na AWS.
O DynamoDB não tem recurso de restrição de unicidade ou auto-incremento além da chave primária, então unicidade em um segundo atributo precisa de um item marcador mais TransactWriteItems, e IDs sequenciais precisam de um UpdateItem de contador atômico seguido de um PutItem, e paginação é baseada em cursor via LastEvaluatedKey/ExclusiveStartKey, em vez do OFFSET/LIMIT do SQL, uma distinção que ainda se sustenta na documentação atual da AWS, embora o limite de ações do TransactWriteItems tenha crescido de 25 para 100 desde então.
O DynamoDB ordena todo resultado de Query pela ordem em árvore B da sort key, ascendente por padrão, invertida com ScanIndexForward=False, então estratégia de ordenação é inteiramente um problema de modelagem de dados, use atributos imutáveis na chave primária e empurre atributos de sort key que mudam para um GSI, faça zero-padding de números embutidos em sort keys de string para que comparação UTF-8 lexicográfica combine com ordem numérica, use KSUIDs para IDs que precisam ser tanto únicos quanto cronologicamente ordenáveis, e posicione itens pai relativos a seus filhos para controlar em que ponta de uma item collection um Query aterrissa.
Filtragem no DynamoDB é mais barata quando acontece via a condição de chave, não depois da leitura, sort keys compostas concatenam um atributo de baixa cardinalidade (como um status enum) com um de alta cardinalidade (como uma data) em uma única sort key, para que um Query filtre em ambos, índices esparsos excluem itens não correspondentes no momento da escrita para que o próprio índice seja o filtro, e FilterExpression é um último recurso, já que é aplicado depois de os itens serem lidos, significando que você paga por todos os itens filtrados fora. Desde novembro de 2025, a AWS adicionou chaves compostas multi-atributo nativas para GSIs, e a técnica de concatenação manual do livro ainda funciona identicamente ao lado disso, mas não é mais estritamente necessária.
Uma vez que um modelo entidade-relacionamento orientado a padrão de acesso primeiro está pronto, DeBrie dá seis regras concretas para transformá-lo em código funcionando, mantenha atributos de indexação (PK/SK) separados dos atributos de aplicação, implemente o modelo de dados só na fronteira da aplicação, nunca reutilize um atributo entre múltiplos índices, marque todo item com um atributo Type, escreva scripts CLI pequenos para depurar padrões de acesso, e só para as maiores tabelas, encurte nomes de atributo para economizar armazenamento.
A modelagem de dados no Cassandra percorre modelo conceitual, queries da aplicação, modelo lógico, modelo físico e uma passagem de dimensionamento, produzindo uma tabela desnormalizada por query em vez de uma tabela normalizada por entidade, trabalhado do início ao fim no exemplo de hotel/reserva do livro, de Q1 a Q9, passando pela aritmética de tamanho de partição e de disco até o schema CQL final.
Single-table design empacota todo tipo de entidade em uma única tabela DynamoDB, de forma que itens relacionados compartilhem uma partition key e possam ser pré-juntados em uma item collection legível em um único Query, o único substituto que o DynamoDB oferece para joins, mas o livro é igualmente explícito sobre seus três custos e as duas situações (aplicações greenfield de evolução rápida, e backends GraphQL) em que esses custos vencem.
A modelagem de dados no DynamoDB roda na direção oposta do design relacional, enumere todo padrão de acesso antes de desenhar uma chave, já que não há joins para recorrer, e o processo de cinco passos do livro (entender a aplicação, DER, padrões de acesso, chave primária, depois índices secundários) hoje é essencialmente a própria orientação oficial da AWS.
O modelo de dados completo do GitHub de Alex DeBrie, nove tipos de entidade, 24 padrões de acesso, uma tabela, mostra a lista de adjacência e a sobrecarga de GSI sobrevivendo ao contato com um design real, três GSIs separados, cada um dedicado a um relacionamento diferente, o mesmo item Repo sobrecarregando GSI2 com dois significados diferentes dependendo de se é um fork, e Users-para-Organizations dividido para que cada direção do muitos-para-muitos ganhe uma estratégia diferente, em vez de um padrão forçado sobre ambas.
Muitos-para-muitos é a forma mais difícil no DynamoDB, porque você quer consultar ambas as direções sem uma tabela de ligação para fazer join, e as quatro estratégias do livro (duplicação rasa, lista de adjacência, grafo materializado, e normalização com múltiplas requisições) formam um gradiente ordenado por quanto dado de relacionamento mutável cada uma consegue tolerar.
Sem joins disponíveis, um relacionamento pai/filho no DynamoDB é modelado escolhendo uma de cinco estratégias por padrão de acesso, embutir os filhos em um atributo complexo, duplicar o dado do pai em cada filho, pré-juntá-los em uma item collection atrás de uma chave primária composta, reconstruir essa collection em um índice secundário, ou compactar uma hierarquia profunda em uma sort key composta, cada uma restringida por uma pergunta específica que o livro faz você responder.
A API inteira do DynamoDB é escrita em cinco tipos de expressão (KeyCondition, Filter, Projection, Condition, Update) coladas por uma sintaxe de placeholder #name/:value que existe porque valores de atributo são tipados e o DynamoDB se recusa a analisá-los a partir de uma string, e a opinião mais afiada do livro neste par de capítulos, "eu não recomendaria usar um ODM no DynamoDB", porque um ORM de estilo relacional esconde exatamente a decisão de padrão de acesso que torna um Query barato em primeiro lugar.
A API do DynamoDB se divide claramente em três níveis de custo, ações baseadas em item (GetItem/PutItem/UpdateItem/DeleteItem, O(1), um item), Query (busca por hash mais busca em árvore B dentro de uma item collection, a operação em torno da qual o single-table design é construído), e Scan (uma varredura de tabela completa que o livro resume em três palavras: "não use Scans"), com o PartiQL adicionado depois como uma sintaxe estilo SQL sobre os mesmos três níveis, não um novo.
Como transformar volume de dados, fator de replicação e topologia em um plano de implantação real, escolhendo SimpleStrategy versus NetworkTopologyStrategy para posicionamento de réplicas com reconhecimento de rack e data center, derivando o número de nós a partir da fórmula de armazenamento do livro e da diretriz de 1 TB/nó, e as decisões de hardware e rede (CPU/RAM, SSD versus HDD, JBOD versus RAID, evitar load balancers) que sustentam cada nó.
O discurso de elevador de cinquenta palavras do Cassandra, desmontado como um mapa de orientação de alto nível antes de qualquer mecanismo específico. Arquitetura peer-to-peer distribuída e descentralizada sem mestre, escalabilidade elástica, alta disponibilidade, consistência ajustável (prévia diante do teorema CAP), o modelo de dados wide column orientado a linhas (e não a colunas), a história de origem Facebook-Dynamo-Bigtable, e o próprio checklist do livro para saber se o Cassandra se encaixa em um projeto.
Por que bancos de dados relacionais esbarram em uma parede na escala da web, o que o teorema CAP de Brewer realmente força você a abrir mão durante uma partição de rede, e por que a arquitetura shared-nothing e AP por padrão do Cassandra, com consistência ajustável por query, é uma resposta deliberada a esse trade-off, não uma limitação.
A maquinaria em disco por baixo do caminho de escrita do Cassandra, como uma memtable acumula escritas em memória e descarrega para uma SSTable imutável, por que SSTables então precisam ser mescladas via compaction (SizeTiered/Leveled/TimeWindow, cada uma um trade-off diferente de carga de trabalho), como Bloom filters permitem que uma leitura pule uma SSTable sem tocar disco, e como tombstones e gc_grace_seconds interagem com a compaction para envelhecer deleções, além de uma nota book-vs-today sobre a Unified Compaction Strategy opt-in do Cassandra 5.0.
Como um cluster Cassandra concorda sobre associação (membership) e posicionamento de dados sem um coordenador: o protocolo gossip e o Phi Accrual Failure Detector para o estado do cluster, snitches para roteamento com reconhecimento de topologia, o token ring e o hashing consistente para posse determinística de partição, e nós virtuais para rebalanceamento equilibrado.
Em uma leitura, o coordenador pede a linha à réplica mais rápida e a toda outra réplica apenas um digest dela, resolve qualquer discordância escolhendo a célula com o timestamp mais recente (last write wins, com desempate lexicográfico), e então repara de forma transparente, via read repair, as réplicas que responderam com dados obsoletos como parte dessa mesma requisição, um mecanismo que é oportunista por design, hoje configurado pela opção de tabela read_repair do Cassandra 4.0 em vez do removido read_repair_chance do livro, e que nunca substitui o repair de anti-entropia agendado.
Uma escrita no Cassandra é rápida porque o coordenador só espera pelo passo commit-log-depois-memtable em cada réplica antes de confirmar, o descarregamento da SSTable acontece depois de o cliente já ter sido informado do sucesso, e transações leves trocam essa velocidade por linearizabilidade rodando uma negociação Paxos completa de quatro round trips (prepare/promise, read/results, propose/accept, commit/ack) restrita a uma única partição.
O Cassandra separa o fator de replicação, definido uma vez por keyspace pelo SimpleStrategy ou pelo NetworkTopologyStrategy recomendado para produção, do nível de consistência, escolhido por query pelo cliente, de modo que um nó coordenador encaminha cada leitura ou escrita para as réplicas donas da partição e retorna assim que ONE, QUORUM (floor(RF/2 + 1)) ou ALL delas responderem, com R + W > RF sendo a fórmula para consistência forte, e o hinted handoff mais o repair cobrindo as réplicas que não responderam.
Explica a busca vetorial nativa do Cassandra 5.0, o tipo CQL VECTOR<type, dimension> que armazena um embedding como um valor de coluna de comprimento fixo normal, e a query ORDER BY ... ANN OF ... que encontra seus vizinhos mais próximos aproximados. Mostra que a busca vetorial não é um subsistema separado, mas sim Storage-Attached Indexing (SAI) aplicado a vetores, a mesma sintaxe CREATE INDEX ... USING 'sai', o mesmo comportamento de anexação por SSTable e de planejador de query do SAI para índices de texto e numéricos, mas apoiada em um grafo JVector (um primo próximo do HNSW, inspirado no DiskANN) em vez de um trie ou uma árvore k-d.
O índice secundário nativo original do Cassandra (2i) e as materialized views, os dois mecanismos para consultar uma coluna que não é chave de partição antes de o Storage-Attached Indexing existir. O fan-out do 2i em todo o cluster e seus limites rígidos de cardinalidade/tombstone, o conserto parcial por SSTable do SASI, e a troca das materialized views entre desnormalização automática e custo no caminho de escrita, além da imaturidade que o próprio livro admite, exatamente a lacuna que o conceito irmão de SAI fecha.
Explica o Storage-Attached Indexing (SAI), o índice secundário integrado ao motor de armazenamento do Cassandra 5.0, o que ele conserta em relação ao índice secundário nativo mais antigo (2i) e às materialized views (precipícios de cardinalidade, falhas de tombstone, custo de armazenamento por coluna, risco de consistência da desnormalização), como o anexo de índice por SSTable funciona mecanicamente, a sintaxe CQL, e seus ângulos mais novos de análise de texto e busca vetorial (ANN), além de onde seus próprios limites ainda te empurram de volta para o design de tabela orientado a query.
O capítulo de redução de memória de Redis in Action cobre três técnicas: codificações de estrutura curta (ziplist/intset, cuja renomeação para listpack no Redis 7.0 é coberta no conceito Core Data Types), sharding de um HASH ou SET lógico entre muitas chaves em shard, e empacotamento de registros de largura fixa em Strings com GETRANGE/SETRANGE/GETBIT/SETBIT; o padrão de HASH em shard ainda é a orientação oficial do Redis hoje, enquanto o caso de uso de SET em shard para contagem única foi substancialmente superado pela contagem aproximada fixa de ~12KB do HyperLogLog.
O Redis Pub/Sub é uma transmissão ao vivo construída sobre SUBSCRIBE/PUBLISH/PSUBSCRIBE sem nenhuma camada de armazenamento por baixo: uma mensagem publicada em um canal com zero subscribers se perde para sempre, at-most-once, sem fila, sem persistência, e sem replay para um cliente reconectando, e uma conexão inscrita fica dedicada a receber mensagens empurradas até que todo canal e padrão sejam desinscritos (RESP2) ou o RESP3 remova essa restrição; o Sharded Pub/Sub do Redis 7.0 (SSUBSCRIBE/SPUBLISH) escala a própria transmissão entre um cluster hasheando canais de shard para os mesmos 16384 hash slots que chaves, sem mudar nenhuma dessas garantias de entrega.
MULTI/EXEC dá ao Redis uma execução serializada e ininterrupta de uma sequência de comandos enfileirada, não uma transação estilo SQL com rollback em erro; um comando que falha durante o EXEC (por exemplo, LPOP contra uma string) não aborta os que vêm depois dele, então a aplicação, não o Redis, precisa decidir de antemão (via DISCARD) se deve rodar de todo; o WATCH sobrepõe um check-and-set de locking otimista, abortando o EXEC se uma chave observada mudou desde a observação, enquanto pipelines resolvem um problema inteiramente diferente: agrupar comandos para cortar idas e voltas de rede, sem nenhuma garantia de atomicidade própria; e os dois são ortogonais: um pipeline pode rodar sem MULTI/EXEC, e o próprio MULTI/EXEC sempre é enviado como um pipeline por baixo dos panos.
Redis Streams (5.0, 2018) são um log persistido e append-only com IDs de entrada ordenados por tempo, lidos via XADD/XRANGE/XREAD; mas o ganho real são os consumer groups (XGROUP, XREADGROUP, XACK, XPENDING, XCLAIM/XAUTOCLAIM), que dão a vários consumidores cooperando confirmação por mensagem e recuperação de crash via uma Pending Entries List, uma garantia at-least-once que nem o fan-out fire-and-forget do Pub/Sub nem uma fila baseada em List simples fornecem nativamente.
Sets, Sorted Sets, Bitmaps e HyperLogLog são quatro estruturas de dados distintas, não só mais comandos; Sets dão pertencimento único O(1) com álgebra de conjuntos de verdade (SINTER/SUNION/SDIFF); Sorted Sets mantêm uma skip list ordenada por score que funciona ao mesmo tempo como leaderboard ranqueado e índice de faixa; Bitmaps reaproveitam uma String como um bit por ID inteiro para análises booleanas compactas; e o HyperLogLog troca exatidão por uma estimativa de cardinalidade fixa de ~12 KB independentemente da escala.
Strings, Lists e Hashes do Redis são três estruturas distintas, não variações de chave-valor; Strings guardam bytes/inteiros/floats com SET/GET/INCR atômicos e um teto de 512 MB; Lists são listas encadeadas de verdade dando push/pop O(1) em qualquer ponta via LPUSH/RPUSH/LPOP/RPOP/LRANGE (O(N) para qualquer outra coisa) e alimentam filas via BRPOP/RPOPLPUSH; Hashes mapeiam campos String para valores String via HSET/HGET/HGETALL/HINCRBY, consolidando campos relacionados (um case study da Instagram cortou 21 GB para aproximadamente 5 GB migrando de chaves String por campo para Hashes); e desde o Redis 7.0 a codificação interna compacta que os dois livros chamam de ziplist é renomeada para listpack.
Redis in Action constrói dois padrões de fila de tarefa ad hoc diretamente a partir de tipos de dado centrais: uma fila FIFO em uma LIST (RPUSH/BLPOP, com várias lists dando faixas de prioridade baratas) e uma fila atrasada/agendada em um ZSET com score de timestamp de execução, com polling manual já que ZSETs não têm pop bloqueante. Os dois permanecem estruturalmente atuais segundo a própria documentação de fila de trabalho do Redis, mas o consumidor BLPOP simples que esta seção do livro usa não tem recuperação de crash para um worker que morre no meio de um job: a lacuna que os consumer groups do Streams (e bibliotecas de produção como Sidekiq, BullMQ, e Redisson) fecham nativamente.
Os padrões de Redis que aparecem em quase toda aplicação web de produção: um HASH mais um ZSET com score para armazenamento de login/sessão e carrinho de compras, cache-aside com SET...EX para páginas renderizadas e uma programação de atualização por linha para linhas de banco de dados que não toleram um TTL fixo, e INCR/sorted sets para contadores de visualização, estatísticas de série temporal e rate limiting; incluindo como o padrão moderno de sliding-window-log estende os contadores de janela fixa do livro.
O modelo de segurança opt-in e plugável do Cassandra, PasswordAuthenticator e o superusuário padrão cassandra/cassandra para autenticação, CassandraAuthorizer e GRANT/REVOKE baseado em papéis para autorização, e TLS via server_encryption_options e client_encryption_options para criptografia de nó a nó e de cliente a nó em trânsito, além de por que a criptografia em repouso no nível de SSTable ainda não existe no Cassandra open source mesmo na 5.0.
A camada de segurança mais antiga e mais ampla do MongoDB, abaixo da criptografia em nível de campo, com autenticação SCRAM e por certificado x.509 (incluindo um tutorial completo do livro, da CA ao cluster), autorização baseada em roles desde os embutidos como readWrite e dbOwner até root, e TLS para criptografia em trânsito, além de por que o Atlas impõe TLS por padrão enquanto o MongoDB autogerenciado ainda vem com ele desligado.
As únicas ferramentas de segurança do Redis antes de 2020 eram uma única senha compartilhada via requirepass, obscuridade via rename-command, firewall em nível de rede, e TLS envolto em stunnel já que o Redis não tinha nenhum nativamente; este conceito cobre por que isso era inadequado e percorre o conserto de verdade: o sistema de ACL do Redis 6.0 (usuários nomeados, regras por comando/categoria, acesso por padrão de chave e por canal, seletores) e TLS embutido (tls-port, mutual TLS por padrão, criptografia de cluster/replicação) que substituíram as duas lacunas.
O Redis vinha sob a licença permissiva BSD 3-Clause de 2009 até 2023, então a Redis Inc. o relicenciou para a dupla RSALv2/SSPLv1 em março de 2024 por causa de provedores de nuvem monetizando o Redis sem contribuir de volta, levando a Linux Foundation a lançar o fork Valkey licenciado sob BSD e com governança multi-fornecedor (apoiado por AWS, Google Cloud e Oracle) apenas oito dias depois; em maio de 2025 a Redis Inc. adicionou a licença AGPLv3 aprovada pela OSI como uma terceira opção de licenciamento para o Redis 8, deixando a escolha atual entre Redis e Valkey como uma troca genuína entre os recursos que o Redis lança primeiro e a licença permissiva e governança multi-fornecedor do Valkey.
Os dois livros-fonte ensinam scripting Lua ad hoc via EVAL/EVALSHA e um wrapper feito à mão de cache-miss de SCRIPT LOAD porque isso era todo o scripting que o Redis tinha em 2013-2015; o Redis Functions (Redis 7.0, 2022) substitui isso por bibliotecas nomeadas e versionadas carregadas via FUNCTION LOAD e invocadas com FCALL/FCALL_RO, que o próprio Redis persiste no AOF e replica em vez de deixar a durabilidade do script para cada aplicação cliente.
O Redis 8 (maio de 2025) dobrou cinco módulos do Redis Stack antes separados e com licenciamento separado para dentro do core do Redis de graça, documentos JSON nativos, busca full-text/vetorial via o Redis Query Engine (FT.CREATE/FT.SEARCH), Time Series (TS.*), e cinco estruturas probabilísticas além do HyperLogLog (Bloom, Cuckoo, Count-min sketch, Top-K, t-digest), mais um tipo de dado nativo totalmente novo, Vector Sets (VADD/VSIM), para busca por similaridade de embeddings, a resposta do Redis para a mesma necessidade de IA/RAG que o MongoDB endereça com Atlas Vector Search.
Vai além do resumo de uma linha em redis-partitioning-and-cluster-fundamentals para rastrear o mecanismo real do Sentinel: quorum só detecta um master falho (SDOWN depois ODOWN), um voto de maioria separado entre todos os processos Sentinel autoriza o failover, e diretivas como down-after-milliseconds, failover-timeout, e parallel-syncs governam cada estágio dessa sequência; mais o padrão de descoberta de cliente ciente do Sentinel, e o próprio posicionamento da documentação atual do Redis do Sentinel como alta disponibilidade especificamente para Redis não clusterizado, já que o Redis Cluster fornece seu próprio failover embutido uma vez que sharding já está em jogo.
O Redis começou sem nenhuma história nativa de distribuição, então o livro percorre os esquemas de particionamento do lado do cliente que as pessoas usavam em vez disso (range, hash, presharding, consistent hashing, e tagging de hash) antes de mostrar os dois sistemas feitos sob medida que os substituíram: o Redis Cluster, que faz sharding de dados entre 16384 hash slots via HASH_SLOT = CRC16(key) mod 16384 (com hash tags {tag} forçando chaves relacionadas para o mesmo slot para operações multi-chave), e o Redis Sentinel, que lida com failover automático baseado em quorum em um par primário/réplica sem sharding, sem distribuir nenhum dado.
Dimensionar um pool de conexões a partir de princípios básicos de RAM, CPU e disco, rotear escritas e leituras por pares frontend-backend do HAProxy com balanceamento leastconn, e multiplexar muitos clientes em poucas conexões reais do PostgreSQL com as trocas de pool_mode session/transaction/statement do PgBouncer.
Ajustes de sysctl e /sys em nível de sistema operacional que mantêm o PostgreSQL online sob estresse, não apenas mais rápido; vm.dirty_background_bytes/vm.dirty_bytes baseados em bytes para evitar um enorme flush de escrita emergencial, vm.swappiness=1 para manter os backends fora do swap, e desativar Transparent Huge Pages, cuja desfragmentação via khugepaged pode travar o PostgreSQL por dezenas de segundos, uma recomendação que a documentação atual do PostgreSQL agora declara explicitamente.
Por que um CREATE INDEX simples bloqueia a tabela contra toda escrita durante toda a construção, como o CREATE INDEX CONCURRENTLY troca isso por uma construção mais lenta, com duas leituras, sem bloquear escritas, suas restrições rígidas (sem bloco de transação, uma construção por tabela, acúmulo de espera por lock em OLTP), e como a documentação oficial de hoje cobre uma lacuna real da receita do livro, o que fazer quando uma construção concorrente falha e deixa um índice INVALID para trás, além do REINDEX CONCURRENTLY, já disponível na própria versão-alvo do livro, o PostgreSQL 12, mas nunca mencionado na receita.
Por que um cache frio do sistema operacional e dos shared buffers depois de um crash ou restart pode deixar um servidor PostgreSQL tecnicamente "de pé" lento a ponto de ser inutilizável, como capturar e recarregar as tabelas e índices mais ativos com pgFincore ou pg_prewarm, e como o background worker autoprewarm do pg_prewarm, disponível bem antes do PostgreSQL 12, automatiza toda a receita de captura e restauração sem uma tabela active_snap montada à mão.
Como a coluna pg_settings.context revela se mudar uma configuração exige um restart completo do postmaster, um reload via SIGHUP, ou um SET de nível superusuário/usuário, como IS DISTINCT FROM lista com segurança configurações alteradas do padrão boot_val, e como o GRANT SET/ALTER SYSTEM ON PARAMETER do PostgreSQL 15 agora permite que administradores deleguem configurações específicas de contexto superusuário a papéis não superusuário sem uma concessão completa de superusuário.
Como definir um postgresql.conf inicial defensável para um servidor de alta disponibilidade (dimensionamento de conexões e memória, ajuste de WAL e checkpoints, preparo para replicação, estimativas de custo do planejador e logging) e quais configurações específicas do livro foram renomeadas, removidas ou viraram o padrão desde 2020 (o hot_standby do wal_level virou replica, wal_keep_segments virou wal_keep_size, o padrão de checkpoint_completion_target agora é 0.9).
Os botões de ajuste que ficam sobre o motor de armazenamento em LSM-tree do Cassandra, caches de key/row/chunk/counter e quando cada um de fato ajuda, throttling e concorrência de throughput de compaction para quando backlogs de SSTable se acumulam, e configurações de heap/GC da JVM, com uma checagem book-vs-today confirmando que o Cassandra 5.0 agora traz G1GC como padrão (não o CMS do livro), já que o CMS foi removido completamente no JDK 14.
Cobre a manutenção conduzida pelo operador que mantém um cluster Cassandra saudável, anti-entropy repair via nodetool repair (completo versus incremental, sequencial versus paralelo, repair de intervalo primário e de sub-intervalo, árvores de Merkle e overstreaming), o ciclo de vida do nó (adicionar nós e data centers, diagnosticar e substituir nós que falharam, decommission/removenode/assassinate em ordem de preferência), e backup/restauração via snapshots e backups incrementais (SSTables com hard link, sstableloader, a ressalva de exclusão do schema).
Como de fato olhar para dentro de um nó Cassandra em execução, a camada JMX/MBean (StorageServiceMBean, CompactionManagerMBean, GossiperMBean) que expõe estado interno, os comandos nodetool construídos sobre ela (status, info, tpstats, compactionstats, tablestats), e o recurso de tabelas virtuais (system_views, system_virtual_schema) que permite consultar esse mesmo estado com CQL puro, além de métricas Dropwizard, configuração de log, e logging completo de query.
O que realmente observar em uma implantação MongoDB em execução (mongostat/mongotop/serverStatus, memória e page faults, dimensionamento do working set, atraso de replicação e duração do oplog, o sistema de tickets do WiredTiger como sucessor da porcentagem de lock) e como tirar os dados com segurança (mongodump/mongorestore vs snapshots de sistema de arquivos/volume, mais as ressalvas de clusters shardeados), com notas de livro vs hoje sobre o Atlas substituindo o Ops Manager como caminho padrão e o fim do suporte a versões antigas no Cloud Manager em 2024.
Um pipeline de monitoramento push-based em que o Telegraf faz polling do PostgreSQL (incluindo SQL customizado arbitrário via o plugin extensível) e envia para o InfluxDB para armazenamento, com o Grafana renderizando dashboards; lag de replication slot, idade de XID até o wraparound e contagens de estado de sessão como as métricas que vale a pena coletar.
Lendo o estado de conexão ao vivo via pg_stat_activity, encontrando consultas de alta frequência (não só lentas) via pg_stat_statements, e rastreando cadeias de bloqueador-para-bloqueado com pg_locks + pg_blocking_pids(); mais o papel predefinido pg_monitor, que substitui as funções wrapper SECURITY DEFINER feitas à mão do livro.
O caminho de escalonamento para expulsar um cliente PostgreSQL mal-comportado, desde consultar pg_stat_activity em busca de sessões de longa duração ou idle-in-transaction, passando pelo mais gentil pg_cancel_backend() e pelo mais contundente pg_terminate_backend(), até um tcpkill em nível de rede quando o cliente se recusa a reconhecer o encerramento; e como o argumento opcional de timeout do PostgreSQL 14 em pg_terminate_backend() agora deixa a própria função esperar pela confirmação em vez de exigir uma consulta manual de reconferência.
Como ranquear tabelas e índices por tamanho (pg_total_relation_size), por atividade de escrita/leitura (pg_stat_user_tables, pg_stat_user_indexes), e por bloat (a extensão pgstattuple), e como as colunas last_seq_scan/last_idx_scan do PostgreSQL 16 resolvem a própria reclamação do livro de que contadores de atividade não têm timestamp associado.
Consultas entre servidores via postgres_fdw (CREATE SERVER, user mappings, foreign tables, IMPORT FOREIGN SCHEMA), construção de um esquema de sharding artesanal com um gerador de ID único empacotado em bits, e por que o Citus é o caminho mais pragmático para sharding horizontal de verdade hoje em dia.
A arquitetura do Patroni baseada em DCS (etcd/ZooKeeper/Consul para consenso, HAProxy para roteamento, cada nó rodando o mesmo loop de reconciliação) para failover totalmente automatizado do PostgreSQL, patronictl switchover para manutenção sem downtime, e por que operadores Kubernetes (CloudNativePG, Zalando postgres-operator) se tornaram um caminho de deployment comum hoje.
Como o repmgr sobrepõe metadados de cluster, clonagem de standby com um comando, quorum baseado em witness e failover automático conduzido pelo repmgrd, tudo por cima da replicação por streaming nativa; requisitos de confiança SSH/sudo, promote_command/follow_command, e como ele se compara ao Patroni para novos deployments hoje.
Como o Barman (backup centralizado de uma frota via SSH/streaming, com capacidade de RPO quase zero) e o pgBackRest (backups autocontidos, comprimidos, incrementais e diferenciais) vão além do pg_basebackup com catálogos, políticas de retenção e PITR, além do suporte nativo a armazenamento em nuvem e da remoção do modo de backup exclusivo no PostgreSQL 15.
Replicação em nível de linha embutida no PostgreSQL desde a v10, CREATE PUBLICATION/CREATE SUBSCRIPTION sem ferramentas de terceiros, por que DDL e sequences não replicam, o requisito de replica identity para UPDATE/DELETE, e o que o PostgreSQL 15-19 adicionou (filtros de linha/coluna, log de conflitos, pg_createsubscriber).
Como o PostgreSQL constrói log shipping, streaming replication, hot standby, replication slots e replicação síncrona todos por cima do WAL; pg_basebackup/pg_receivewal, FIRST vs. ANY em synchronous_standby_names, hot_standby_feedback, e válvulas de segurança de retenção de WAL baseadas em slot.
Por que corrupção silenciosa de CPU/RAM pode envenenar um primário síncrono e seu standby quase simultaneamente, como arquivar WAL para um servidor terciário com um atraso deliberado de uma hora via cron/mtime ganha tempo para detectar isso antes que se espalhe, por que o próprio exemplo de recovery_min_apply_delay do livro não atrasa de fato uma hora, e por que data checksums (não os vagos "monitores" do livro) são o mecanismo de detecção real do qual esta receita depende.
Como aplicar patches no PostgreSQL nos dois nós de um par primário/réplica sem nunca tirar o banco totalmente do ar, atualizando primeiro a réplica ociosa, promovendo-a a novo primário, e depois reconstruindo o antigo primário como uma réplica nova; além de por que o pg_rewind, já disponível na própria versão-alvo PostgreSQL 12 do livro, substitui a recópia completa e desperdiçada via pg_basebackup do nó rebaixado que a receita usa.
Como mover um banco de dados PostgreSQL para um hardware novo com downtime mínimo construindo uma réplica de streaming com pg_basebackup, esperando ela se atualizar, e então promovendo-a a primário; mais duas lacunas na própria receita da era PostgreSQL 12 do livro: as colunas sent_location/replay_location do pg_stat_replication já haviam sido renomeadas para sent_lsn/replay_lsn desde o PostgreSQL 10, e a flag -R do pg_basebackup já automatizava a configuração manual de standby.signal/primary_conninfo que a receita percorre à mão.
Como traduzir "quem usa o banco de dados e o que vai tolerar" em uma meta concreta de uptime expressa em noves, e como a checklist ad-hoc de SLA do livro mapeia para o vocabulário formal de hoje de SLI/SLO/SLA/error-budget e os benchmarks publicados de SLA de Postgres gerenciado (AWS RDS, Google Cloud SQL).
Como o PostgreSQL multi-master troca o problema de failover/quorum por um problema de evitar conflitos de escrita, por que o overhead da topologia em malha cresce como C = N*(N-1), e como o enquadramento genérico de "software proprietário" do livro de 2020 mapeia para o EDB Postgres Distributed de hoje e o pgEdge/Spock recém open source.
Como o fencing garante que um primário rebaixado ou isolado do PostgreSQL genuinamente não consiga mais aceitar escritas (STONITH, desligamento remoto de energia, e SMITH, autodesligamento sob isolamento), e como o watchdog embutido do Patroni agora automatiza a metade SMITH via um dispositivo softdog/watchdog de hardware do Linux em vez de uma checagem de isolamento feita à mão.
Como regras de votação com witness evitam eleições empatadas durante failover automatizado, e como a indireção de conexão (DNS, VIP, multiplexador de conexão ou load balancer) mantém as aplicações livres de reconfiguração quando o primário muda; hoje, tipicamente HAProxy fazendo polling na própria API REST de health-check do Patroni.
Como derivar quantos nós PostgreSQL um cluster de alta disponibilidade precisa (backup, réplicas, witness) e onde posicioná-los entre data centers, e como o quorum baseado em DCS do Patroni substituiu o nó witness posicionado manualmente.
Como o Recovery Point Objective (tolerância a perda de dados) e o Recovery Time Objective (tolerância a indisponibilidade) conduzem decisões de arquitetura do PostgreSQL, e como o commit por quorum da replicação síncrona transforma uma meta de RPO em uma garantia aplicável.
Migrar para o Cassandra é um exercício de remodelagem, não uma porta tabela por tabela: os padrões de tradução direta do livro (entidades para tabelas, tabelas de junção para tabelas mapeadas ou UDTs colapsados, tipos soltos para UDTs) ainda precisam passar pela metodologia orientada a query, mais o padrão strangler para adaptar a aplicação e dual-write/CDC para um corte sem downtime. Integrando para fora, o conector sink DataStax do Kafka Connect transmite eventos para o Cassandra (com o CDC de saída permanecendo a direção mais difícil, ainda parcialmente resolvida), e o spark-cassandra-connector roda analytics com dados locais sobre tabelas Cassandra sem um pipeline de ETL separado, além do que mudou desde 2022 agora que o conector Spark passou para governança Apache e o Debezium fechou em grande parte a lacuna de CDC de saída que o livro deixava aberta.
Construindo um cliente Cassandra real com o driver Java, um único CqlSession de vida longa criado com o builder fluente, por que PreparedStatement supera SimpleStatement por mais do que conveniência (preparação única, roteamento com reconhecimento de token, proteção contra injeção), o QueryBuilder e o mapeador de objetos orientado a anotações como alternativas de nível mais alto, e execução assíncrona com CompletionStage. Além disso, o que mudou desde a cobertura do livro voltada para a versão 4.0, agora que o driver é governado pela Apache sob org.apache.cassandra na linha 4.19.x.
O modelo de dados do Cassandra construído de baixo para cima, de coluna para linha, para partição, para tabela, para keyspace, para cluster, com a chave primária composta (partition key mais clustering columns) que decide tanto o posicionamento nos nós quanto a ordem em disco, timestamps e TTL por coluna, e o sistema de tipos CQL completo, dos tipos numéricos e textuais até uuid/timeuuid, counters, as três coleções, tuplas e tipos definidos pelo usuário congelados.