Invalidar o preço após uma escrita confirmada

RedisBeginner
Pratique Agora

Introdução

Uma alteração de preço feita pela equipe deve atualizar o banco de dados oficial e remover o valor antigo em cache desse preço exato. Limpar todos os produtos em cache descartaria trabalho útil. Remover uma entrada de cache antes de uma escrita com falha na origem também representaria incorretamente o resultado da escrita.

Este lab independente prepara um novo posto de cache e uma política correta de leitura com identidade completa e 15 segundos. Você precisa dos conceitos de cache-aside, identidade, TTL e do pequeno modelo Python do lab anterior. Você concluirá change_price, observará uma escrita rejeitada no primário e comparará a invalidação explícita com uma atualização externa que se torna visível após a expiração natural. Apenas Terminal é necessário; nenhum arquivo ou processo é transferido da VM anterior.

Edite apenas /home/labex/project/cache-desk/policy.py, usando as três funções documentadas e a API de origem/cache. Mantenha todas as ferramentas, as identidades das tabelas de origem, os preços não relacionados e a nota do catálogo. As alterações permitidas no primário são North MUG USD de 1200 para 1350 por meio de sua política, uma tentativa rejeitada de preço negativo e a alteração externa preparada para 1450. As solicitações são executadas sequencialmente com serviços saudáveis no percurso do aluno; isto não é consistência forte, invalidação distribuída de cache nem uma promessa de que dois bancos de dados confirmem atomicamente. O tempo estimado é a margem do autor para leitura e erros, sem dados de cronometragem com iniciantes.

Invalidar o preço exato após o commit

Nesta etapa, você fará uma escrita bem-sucedida na origem remover seu preço desatualizado em cache, preservando o cache após uma escrita rejeitada.

Comece em Terminal. Entre no espaço de trabalho preparado desta unidade independente:

cd /home/labex/project/cache-desk
./desk status
./desk policy

Os três campos de identidade são loja, SKU e moeda. North MUG USD começa em 1200 centavos. South MUG USD é 1500, North MUG EUR é 1100, e North FREE USD é 0. Carregue um preço normal no cache e envie uma alteração legítima:

./desk view north MUG USD
./desk view north MUG USD
./desk change north MUG USD 1350
./desk status
./desk view north MUG USD

O status agora mostra o preço confirmado 1350 no primário, enquanto uma consulta feita logo em seguida ainda pode retornar 1200 do cache. Se seus 15 segundos já passaram, a consulta pode corretamente não encontrar a chave e mostrar 1350; a invalidação ausente continua sendo um defeito de política, mesmo quando a expiração o oculta. A mensagem de sucesso diz apenas que a política enviada retornou; observações reais do primário e do cache determinam o que ocorreu.

change_price já chama source.write(identity, cents). Esse método preparado atualiza uma linha conhecida do primário em uma transação nativa do SQLite. Ele retorna apenas após o commit. Um preço negativo viola a restrição CHECK da origem e gera um erro; uma identidade desconhecida gera um erro de busca. O Python executa a próxima linha apenas se a chamada anterior retornar com sucesso. Portanto, coloque a exclusão exata do Redis após essa chamada. cache_key(prefix, identity) reutiliza o mesmo mapeamento de identidade da função de leitura. Não exclua apenas pelo SKU nem use FLUSHDB/FLUSHALL.

Como no lab anterior, o here-document entre aspas substitui apenas policy.py; preserve a indentação. A função completa de leitura é repetida para que a substituição seja completa:

cat > policy.py <<'POLICY'
def cache_key(prefix, identity):
    return f"{prefix}:{identity[0]}:{identity[1]}:{identity[2]}"


def read_price(source, cache, prefix, identity):
    key = cache_key(prefix, identity)
    value = cache.command("GET", key)
    if value is not None:
        return int(value), "cache"
    cents = source.read(identity)
    cache.command("SET", key, cents, "EX", 15)
    return cents, "source"


def change_price(source, cache, prefix, identity, cents):
    source.write(identity, cents)
    cache.command("DEL", cache_key(prefix, identity))
POLICY

Carregue dois preços não relacionados no cache e repita a escrita permitida. Gravar os mesmos 1350 ainda executa um commit real de linha conhecida e exercita a invalidação:

./desk view south MUG USD
./desk view north MUG EUR
./desk change north MUG USD 1350
./desk inspect north MUG USD
./desk view north MUG USD
./desk view north MUG USD

A inspeção deve mostrar a chave-alvo ausente imediatamente após a escrita. A próxima consulta retorna source/1 e 1350; a seguinte é cache/0. Outras chaves não são invalidadas deliberadamente; ainda podem expirar naturalmente. Use status para distinguir valores preservados no primário da presença no cache.

Agora solicite deliberadamente um preço negativo inválido. Espera-se que o comando a seguir termine com status diferente de zero e imprima IntegrityError com a falha nativa de CHECK. Isso não é uma atualização bem-sucedida, uma ausência no cache nem um problema de conexão:

./desk change north MUG USD -1
./desk status
./desk inspect north MUG USD
./desk view north MUG USD

O preço oficial permanece 1350. Se a entrada carregada não expirou naturalmente, seu valor e prazo original permanecem, e a consulta é cache/0. Um leitor lento pode, em vez disso, ver uma expiração normal; a verificação isolada testa a preservação após escrita com falha sem uma corrida contra o tempo de leitura. O Python para antes de DEL quando source.write gera uma exceção. Não capture o erro e imprima um sucesso falso.

Se o shell informar que ./deskk está ausente, execute ls e corrija o nome. Se sua política tiver um erro de sintaxe ou indentação, use ./desk policy para inspecioná-la e substitua-a novamente pelo bloco completo. Um comando com falha de preço negativo não exige reinicialização: a transação preparada da origem já desfez essa tentativa. Em seguida, faça uma leitura, não uma edição direta do banco de dados.

Deixar uma atualização externa chegar ao próximo preenchimento

Nesta etapa, você observará uma alteração externa real na origem e o tempo de vida limitado de um valor antigo em cache.

Continue em Terminal, em /home/labex/project/cache-desk. Reaplique o preço confirmado permitido para remover qualquer cache-alvo existente e depois preencha 1350. Isso deixa o ponto inicial claro sem alterar outro produto:

./desk change north MUG USD 1350
./desk view north MUG USD
./desk inspect north MUG USD

O comando externo representa um gravador deliberadamente controlado que ignora o gancho de invalidação desta aplicação. Ele executa um commit real no primário, não uma mudança de exibição acionada por temporizador. Esta unidade permite apenas o alvo externo e novo preço a seguir:

./desk external north MUG USD 1450
./desk status
./desk view north MUG USD
./desk inspect north MUG USD

O status mostra 1450 imediatamente. Uma consulta antes de a chave existente expirar ainda pode retornar 1350 do cache com zero leituras do primário. Seu prazo continua diminuindo; não reinicia no acerto. Se o tempo gasto lendo já excedeu o tempo de vida, ver 1450 após uma ausência no cache é esperado. Você pode repetir prontamente a sequência inicial de alteração/preenchimento/escrita externa desta etapa para observar o intervalo desatualizado.

Espere além do tempo de vida completo configurado para o preenchimento antes da leitura final:

sleep 16
./desk inspect north MUG USD
./desk view north MUG USD
./desk view north MUG USD
./desk status

A inspeção mostra a chave ausente. A próxima consulta lê 1450 confirmado do primário e preenche o Redis; a consulta seguinte pode usar esses 1450 realmente em cache. Mantenha esse estado final do primário. Todos os outros preços oficiais permanecem 1500, 1100 e 0. ./redis GET catalog:admin:note permite inspecionar a nota não relacionada preservada, sem fazer uma solicitação ao primário.

Esta é uma política cache-aside sequencial. Uma atualização externa após um preenchimento pode deixar o valor antigo desse preenchimento visível até seu prazo existente de 15 segundos. Essa observação não comprova um limite global de desatualização sob leituras e escritas concorrentes, preenchimentos lentos ou um serviço parado. Um leitor concorrente poderia buscar um valor antigo na origem antes de uma escrita e preenchê-lo após a invalidação; este curso não resolve essa condição de corrida nem uma avalanche de ausências simultâneas no cache.

O commit na origem e a exclusão no Redis são operações separadas. Se o Redis recusar a exclusão após um commit bem-sucedido, o primário já foi alterado. O erro deve se propagar como falha de operação de cache; não deve ser descrito como rollback do primário nem como invalidação concluída. O avaliador isolado testa essa falha nativa sem interromper seu serviço real. Novas tentativas em produção, invalidação durável e recuperação de indisponibilidade precisam de projeto adicional, fora deste exercício.

Resumo

Você ligou um commit real no primário à invalidação exata do cache, preservou os dados em cache após uma escrita rejeitada na origem e observou uma alteração externa por meio da expiração natural. Sua política distingue uma falha de escrita na origem de uma falha posterior no Redis e explicita seu limite de consistência sequencial.