Resumo

  • atomic:true obriga o servidor a confirmar todas as criações, atualizações e destruições de um Foo/set ou nenhuma; a incapacidade deve produzir cannotApplyAtomically, nunca aplicação parcial silenciosa.
  • Retentar sem atomicidade não é recuperação equivalente. É uma decisão de aceitar estados intermediários e precisa de autoridade, justificativa e reconciliação próprias.

O pedido parecia simples: trocar dois nomes, atualizar uma referência e remover um objeto antigo. Cada operação era possível isoladamente. O problema estava no intervalo entre elas. Uma ordem produzia colisão de nomes; outra deixava a referência apontando para o objeto errado; uma falha tardia criava um estado que nenhum responsável havia aprovado.

O cliente marcou atomic:true. O servidor, porém, não conseguia incluir aquela combinação inteira em uma única unidade de commit. A resposta correta não era fazer o que pudesse. Era declarar que não podia oferecer a garantia solicitada e não mudar nada.

Essa fronteira aparece em JMAP Conditional Set. A revisão 00 tem data de 15 de setembro de 2026 e expira em 19 de março de 2027. No corte da pesquisa, era um Internet-Draft ativo do grupo JMAP, destinado ao Standards Track e proposto para atualizar o RFC 8620 se aprovado. Não é RFC final, medição de implantação ou certificado de produto.

O documento trata de dois problemas relacionados, mas diferentes. ifUnchangedBy permite que uma mudança dependa de propriedades específicas de objetos específicos. atomic:true decide se as mudanças pedidas no mesmo método podem aparecer juntas. Uma protege o ponto de partida declarado; a outra protege a unidade de commit.

Condições estreitas reduzem conflitos falsos

O JMAP Core já oferece ifInState, uma condição sobre a cadeia de estado de todo o tipo de objeto na conta. Qualquer alteração desse tipo pode trocar a cadeia, mesmo que o objeto relevante permaneça intacto. Em contas movimentadas, isso transforma atividade normal em rejeições e leituras repetidas.

ifUnchangedBy recebe um mapa de IDs para PatchObjects. Para cada ponteiro, o servidor compara o valor atual com o esperado. A condição é satisfeita quando aplicar o PatchObject não produziria mudança. null afirma ausência, e a representação usada é a que Foo/get retornaria.

Essa precisão permite dizer: substitua o conteúdo somente se blobId ainda for G_old; retire o compartilhamento somente se mayRead continuar verdadeiro; exclua a mensagem somente se $seen continuar ausente. Mudanças não citadas podem ser ignoradas de propósito.

Por isso o sucesso não prova que o objeto inteiro permaneceu igual. Prova apenas que os ponteiros selecionados correspondiam no início do método. Um sistema de auditoria precisa guardar essa seleção. O rótulo “versão confirmada” aumenta o alcance da evidência sem fundamento.

Uma comparação do objeto inteiro só surge quando existe um token mantido pelo servidor que muda em toda modificação. Mesmo assim, é a disciplina de atualização do token que sustenta a afirmação. Uma rota administrativa que não o avance cria uma janela invisível.

O contexto pode ser condicionado sem virar soberania

A condição pode mencionar um objeto do mesmo tipo que o método não altera. Uma atualização de arquivo pode exigir que o arquivo continue no pai d3 e que d3 mantenha o nome Reports. As condições são avaliadas no começo do método, antes de suas próprias mutações.

Isso evita gravar conteúdo correto no lugar errado após um movimento concorrente. Não prova toda a hierarquia, a política de retenção, o mandato do operador ou o impacto sobre serviços externos. O conjunto de propriedades escolhido continua sendo um recorte.

Propriedades definidas pelo servidor também podem participar, desde que o cliente possa lê-las. Se não puder, o servidor deve responder forbidden, e não avaliar a hipótese. Caso contrário, a sequência de sucessos e falhas vira um oráculo para valores ocultos.

Poder testar um fato não concede o direito de agir. A autorização da mutação é separada e pode falhar depois que todos os valores coincidirem. Estado e mandato são recibos diferentes.

Quando uma condição falha, todo o Foo/set é rejeitado com stateMismatch; nenhuma criação, atualização ou destruição ocorre, e a cadeia do tipo não muda. Quem deseja resultados independentes usa chamadas separadas. Essa escolha define qual grupo compartilha destino e deve ser tomada pela aplicação que conhece o dano da parcialidade.

Atomicidade avalia o conjunto final

Sem a opção nova, elementos de um Foo/set podem ter resultados independentes. Com atomic:true, todos são confirmados ou nenhum é. Restrições são verificadas no estado final produzido por todas as mudanças, não em cada estado intermediário.

O exemplo mais claro é a troca entre current.txt e previous.txt. Alterar um nome de cada vez viola a unicidade enquanto o outro ainda ocupa o destino. Avaliar os dois juntos mostra que o conjunto final não tem colisão.

Se qualquer elemento falhar por patch inválido, permissão, restrição ou outro SetError, o servidor retorna atomicFailure e não confirma nada. Mapas opcionais podem identificar a causa. Objetos não listados nesses mapas também não foram aplicados; apenas não causaram a rejeição.

Uma condição falsa continua usando stateMismatch, inclusive em método atômico. Assim a operação distingue um ponto de partida que deixou de existir de um conjunto de mudanças que não pode ser validado como unidade.

A recusa explícita é parte da segurança

O servidor pode anunciar a capacidade e ainda não conseguir aplicar uma chamada específica de modo atômico. O método pode exceder a unidade transacional disponível ou consumir recursos por tempo excessivo. Nessa situação, deve retornar cannotApplyAtomically, sem fazer mudança e sem cair para execução parcial.

O erro não é uma inconveniência a ser escondida. Ele informa que a propriedade pedida não está disponível para aquele caso. Se a biblioteca cliente intercepta o erro e repete sem atomic, o registro final parece sucesso, enquanto a garantia original desapareceu.

O texto permite repetir de forma não atômica quando o cliente tolera aplicação parcial. A palavra decisiva é “tolera”. Isso exige conhecer estados intermediários, compensações e impacto. A repetição fraca é uma nova autorização, não um pacote retransmitido.

Para ações de alto impacto, o sistema deve preservar o pedido recusado, a razão da incapacidade, o principal que aceitou enfraquecer a garantia, a sequência não atômica executada e o resultado da reconciliação. Sem isso, a disponibilidade ganha apagando o contrato.

Métodos atômicos grandes também podem manter transações ou bloqueios por mais tempo. Limites existentes de objetos continuam aplicáveis, e o servidor pode rejeitar uma unidade excessiva. “Tudo ou nada” não significa “tamanho ilimitado” nem “sem custo de contenção”.

O commit não inclui todos os observadores

Mesmo quando o servidor oferece atomicidade, sua fronteira é o Foo/set respondido. Índices, caches, notificações, réplicas, trabalhadores e clientes desconectados não entram automaticamente na mesma transação.

Depois de uma troca de nomes, um cache de caminhos pode mostrar o vínculo antigo. Depois de uma revogação condicional, uma credencial emitida por outro sistema pode continuar ativa por um intervalo. Depois de uma destruição, uma cópia offline pode permanecer.

Isso não torna falsa a resposta do servidor. Mostra que o resultado de negócio é composto. A evidência defensável separa principal autenticado, mandato, propriedades condicionadas, método confirmado, leitura posterior, geração vista por cada consumidor e efeito final.

A disciplina de camada fina de Heng Lu impede que um mecanismo técnico adquira poderes sobre consequências que não observa. JMAP pode coordenar condições e commits. Não deve ser transformado em autoridade sobre o ativo, o usuário ou o serviço externo.

Os testes precisam exercitar as recusas. Faça uma condição falhar e confirme ausência total de mudança. Provoque erro em um elemento atômico e verifique que os demais não escaparam. Use um servidor incapaz daquela unidade e exija cannotApplyAtomically. Depois repita de forma não atômica apenas em ambiente onde estados intermediários e compensação sejam observáveis.

O servidor que recusou a atomicidade não falhou em silêncio. Ele preservou a diferença entre o que podia executar e o que podia garantir. A liderança deve preservar a mesma diferença quando decide o próximo passo.

Fontes