Desenhar uma sequência verificável
O conjunto pedagógico começa com seis linhas e duas chaves de ordenação: sort_key e id. Os ids 1 e 2 têm ambos sort_key=10. A consulta ordena pelos dois campos e devolve dois ids por página. Antes de introduzir alterações, o guião confirma [1,2] e [3,4]. A seguir usa uma segunda sessão para alterar dados entre consultas, com a ordem dos comandos controlada. Isto não é uma corrida dependente do escalonador nem tráfego HTTP. É uma sequência SQL real que permite atribuir cada mudança no resultado a uma alteração conhecida. Conserva essa ordem na reprodução de um incidente antes de discutir uma correção.
Observar a deslocação do offset
Depois da primeira página, a segunda sessão insere id 7 com sort_key=5. A consulta OFFSET 2 passa a devolver [2,3]: id 2 já tinha sido visto. Num ensaio separado, apagar id 1 depois da primeira página faz a mesma consulta devolver [4,5], saltando id 3 que ainda não tinha aparecido. O LIMIT continua correto em ambos os casos, porque cada resposta tem no máximo duas linhas. O problema está no significado da posição quando a população muda. Deduplicar ids no cliente pode esconder a repetição, mas não recupera uma linha omitida. O contrato deve indicar se aceita esta instabilidade ou exige outra forma de percorrer dados.
Guardar a fronteira completa
O cursor do exemplo conserva (sort_key,id). Depois de [1,2], o predicado (sort_key,id)>(10,2) devolve [3,4], mesmo após inserir uma linha antes dessa fronteira. Outro grupo reduz a página a um item. Se guardar apenas sort_key=10 e pedir sort_key>10, a resposta é id 3 e omite id 2, que partilha a mesma chave. Com a fronteira (10,1), a resposta correta é id 2. Não basta acrescentar id ao ORDER BY se o cursor o perde. A comparação e a ordem precisam de representar a mesma sequência. Neste laboratório ambos os campos são não nulos; uma expressão separada mostra por que motivo NULL merece um contrato próprio.
Testar chaves que podem mudar
O cursor não torna a chave imutável. O guião altera sort_key de id 1 para 60 depois de esse id aparecer. A consulta seguinte após (10,2) volta a encontrá-lo no fim. Num caso separado, id 3 ainda não visto passa para sort_key=5 e desaparece do restante percurso. São dois efeitos diferentes da mesma liberdade de atualização. Se a listagem ordena por última modificação, este comportamento pode ser inerente à experiência pretendida. Uma exportação completa pede outro contrato. Avalia uma chave estável, uma visão conservada ou um resultado materializado, consoante a necessidade, sem transformar keyset numa promessa universal de leitura sem omissões.
Separar progresso de fotografia
O ensaio guarda max(id)=6 como limite superior e depois insere id 7. Esse limite exclui o novo id, mas uma alteração de units em id 4 de 400 para 999 continua visível. O relatório mostra as duas propriedades lado a lado. Um watermark pode ajudar a definir uma população, mas não conserva automaticamente os valores dessa população. Também não prova que a ordem dos ids corresponde à ordem dos commits num sistema real. Num ADR, escreve que campo marca o progresso, como é atribuído e que alterações são permitidas. Se o consumidor precisa dos valores de um instante comum, inclui uma estratégia de visão consistente e valida-a separadamente.
Aplicar ao diagnóstico de APS
Num incidente fictício, utilizadores exportam operações em várias páginas e reportam ids repetidos. Pede a ordenação efetiva, o tamanho da página, o predicado, o cursor recebido e uma linha temporal das alterações. Distingue uma repetição criada pelo servidor de uma nova tentativa da mesma página pelo cliente. Não concluas que remover duplicados resolve completude. Define também como retomar uma exportação interrompida e que população o resultado pretende representar. O laboratório oferece sequências pequenas para discutir essas hipóteses com desenvolvimento e negócio. A aplicação real ainda precisa de validação de autorização, erros de transporte, evolução do contrato e carga representativa.
python3 content/labs/design-pagination/run.py --postgres-prefix /path/to/postgresql-18.6 --output /tmp/dr-pages-new.json
# Choose a new output path. The script creates and stops its own private cluster.Uma inserção antes da primeira página faz OFFSET repetir o id 2; apagar uma linha anterior faz saltar o id 3.
Armadilhas comuns
Ordem sem desempate, cursor com apenas timestamp, chave de ordenação mutável e promessa de snapshot sem conservar a mesma visão.
Tópicos relacionados: Dados e consistência · Contratos de API
Um cursor define uma fronteira de pesquisa. A identidade, a estabilidade da ordem e a visão dos dados determinam o que aparece depois.
Referência: LIMIT and OFFSET · System design patterns; PostgreSQL18 scoped examples; primary guidance consulted 2026-09-30