소개
직원의 가격 변경은 기준 데이터베이스를 갱신하고 정확히 그 가격의 오래된 캐시 값을 제거해야 합니다. 캐시된 상품을 전부 지우면 유용한 작업이 버려집니다. 실패한 원본 쓰기 전에 캐시 항목을 제거하는 것도 쓰기 결과를 잘못 나타냅니다.
이 독립 실습에는 새 캐시 데스크와 올바른 완전 식별 정보·15초 읽기 정책이 준비되어 있습니다. 앞 실습의 cache-aside, 식별 정보, TTL, 짧은 Python 템플릿 개념이 필요합니다. change_price를 완성하고 거부된 기본 데이터베이스 쓰기를 관찰하며, 명시적 무효화와 자연 만료 후 보이는 외부 갱신을 비교합니다. Terminal만 필요하며 이전 VM의 파일이나 프로세스는 이어지지 않습니다.
문서화된 세 함수와 원본/캐시 API로 /home/labex/project/cache-desk/policy.py만 편집하세요. 모든 도구, 원본 테이블 식별 정보, 무관한 가격, 카탈로그 메모를 유지하세요. 허용되는 기본 가격 변경은 정책을 통한 North MUG USD 1200에서 1350으로의 변경, 거부되는 음수 가격 시도, 준비된 외부 변경 1450입니다. 학습 경로의 요청은 정상 서비스에서 순차적으로 실행합니다. 이는 강한 일관성, 분산 캐시 무효화, 두 데이터베이스의 원자적 커밋을 약속하는 것이 아닙니다. 예상 시간은 저자가 읽기와 오류를 고려한 값이며 초보자 시간 측정 데이터는 없습니다.
커밋 후 정확한 가격 무효화하기
이 단계에서는 성공한 원본 쓰기가 오래된 캐시 가격을 제거하게 하되 거부된 쓰기 뒤에는 캐시를 보존합니다.
Terminal에서 시작하세요. 이 독립 단원의 준비된 작업 공간으로 이동하세요:
cd /home/labex/project/cache-desk
./desk status
./desk policy
세 식별 필드는 매장, SKU, 통화입니다. North MUG USD는 1200센트로 시작합니다. South MUG USD는 1500, North MUG EUR는 1100, North FREE USD는 0입니다. 정상 가격을 캐시에 올린 뒤 적법한 변경을 제출하세요:
./desk view north MUG USD
./desk view north MUG USD
./desk change north MUG USD 1350
./desk status
./desk view north MUG USD
이제 상태는 커밋된 기본 가격 1350을 보여 주지만 신속히 조회하면 여전히 캐시된 1200을 반환할 수 있습니다. 이미 15초가 지났다면 조회가 정상 미스로 1350을 보여 줄 수 있습니다. 만료 때문에 드러나지 않더라도 무효화가 없는 것은 정책 결함입니다. 성공 메시지는 제출한 정책이 반환되었다는 뜻일 뿐이며 실제 기본 데이터베이스와 캐시 관찰이 무엇이 일어났는지 결정합니다.
change_price는 이미 source.write(identity, cents)를 호출합니다. 준비된 이 메서드는 SQLite 자체 트랜잭션에서 알려진 기본 행 하나를 갱신하며 커밋 후에만 반환합니다. 음수 가격은 원본의 CHECK 제약 조건을 위반해 오류를 일으키고, 알 수 없는 식별 정보는 조회 오류를 일으킵니다. Python은 앞 호출이 성공적으로 반환한 경우에만 다음 줄을 실행합니다. 따라서 정확한 Redis 삭제를 이 호출 뒤에 두세요. cache_key(prefix, identity)는 읽기 함수와 같은 식별 대응을 재사용합니다. SKU만으로 삭제하거나 FLUSHDB/FLUSHALL을 사용하지 마세요.
앞 실습처럼 따옴표로 지정한 here-document는 policy.py만 덮어씁니다. 들여쓰기를 보존하세요. 완전한 덮어쓰기가 되도록 전체 읽기 함수를 반복합니다:
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
무관한 가격 두 개를 캐시에 올리고 허용된 쓰기를 반복하세요. 같은 1350을 써도 알려진 행의 실제 커밋을 수행하고 무효화를 시험합니다:
./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
쓰기 직후 inspect는 대상 키가 없다고 보여야 합니다. 다음 조회는 source/1과 1350을 반환하며 그다음은 cache/0입니다. 다른 키를 의도적으로 무효화하지 않지만 자연 만료는 계속될 수 있습니다. status로 보존된 기본 값과 캐시 존재 여부를 구분하세요.
이제 잘못된 음수 가격을 의도적으로 요청하세요. 다음 명령은 0이 아닌 상태로 종료하고 SQLite 자체 CHECK 실패와 IntegrityError를 출력하는 것이 예상 결과입니다. 성공한 갱신, 캐시 미스, 연결 문제가 아닙니다:
./desk change north MUG USD -1
./desk status
./desk inspect north MUG USD
./desk view north MUG USD
기준 가격은 1350으로 남습니다. 캐시에 올린 항목이 자연 만료되지 않았다면 값과 원래 시한이 유지되고 조회는 cache/0입니다. 천천히 읽으면 정상 만료를 볼 수도 있습니다. 격리 검사는 읽기 시간과의 경주 없이 실패한 쓰기 보존을 시험합니다. source.write가 예외를 일으키면 Python은 DEL 전에 멈춥니다. 오류를 잡아 거짓 성공을 출력하지 마세요.
셸이 ./deskk가 없다고 보고하면 ls를 실행해 이름을 고치세요. 정책에 구문이나 들여쓰기 오류가 있으면 ./desk policy로 확인하고 전체 블록으로 다시 덮어쓰세요. 실패한 음수 가격 명령은 초기화가 필요하지 않습니다. 준비된 원본 트랜잭션이 이미 그 시도를 롤백했습니다. 직접 데이터베이스를 편집하지 말고 읽기로 이어가세요.
외부 갱신을 다음 캐시 채우기에 반영하기
이 단계에서는 실제 외부 원본 변경과 오래된 캐시 값의 제한된 수명을 관찰합니다.
계속 Terminal의 /home/labex/project/cache-desk에서 작업하세요. 허용된 커밋 가격을 다시 적용해 기존 대상 캐시를 제거하고 1350으로 채우세요. 다른 상품을 바꾸지 않고 시작점을 명확히 합니다:
./desk change north MUG USD 1350
./desk view north MUG USD
./desk inspect north MUG USD
외부 명령은 이 애플리케이션의 무효화 훅을 우회하는 의도적으로 통제된 쓰기 도구를 나타냅니다. 타이머로 표시만 바꾸는 것이 아니라 실제 기본 데이터베이스 커밋을 수행합니다. 이 단원은 다음 외부 대상과 새 가격만 허용합니다:
./desk external north MUG USD 1450
./desk status
./desk view north MUG USD
./desk inspect north MUG USD
상태는 즉시 1450을 보여 줍니다. 기존 키가 만료되기 전 조회는 기본 데이터베이스 읽기 없이 캐시된 1350을 반환할 수 있습니다. 시한은 계속 줄어들며 적중으로 다시 시작하지 않습니다. 읽는 데 쓴 시간이 이미 수명을 넘었다면 미스로 1450이 보이는 것은 예상된 결과입니다. 오래된 값이 보이는 구간을 관찰하려면 이 단계의 초기 변경/채우기/외부 쓰기 순서를 신속히 반복할 수 있습니다.
설정된 전체 채우기 수명보다 오래 기다린 뒤 마지막으로 읽으세요:
sleep 16
./desk inspect north MUG USD
./desk view north MUG USD
./desk view north MUG USD
./desk status
inspect는 키가 없다고 보여 줍니다. 다음 조회는 기본 데이터베이스에서 커밋된 1450을 읽고 Redis를 채웁니다. 이어지는 조회는 실제 캐시된 1450을 사용할 수 있습니다. 이 최종 기본 상태를 유지하세요. 다른 모든 기준 가격은 1500, 1100, 0으로 남습니다. ./redis GET catalog:admin:note로 기본 데이터베이스 요청 없이 보존된 무관한 메모를 살펴볼 수 있습니다.
이는 순차적인 cache-aside 정책입니다. 채우기 후 외부 갱신이 일어나면 그 채우기의 기존 15초 시한까지 오래된 값이 보일 수 있습니다. 이 관찰은 동시 읽기, 쓰기, 느린 채우기, 중단된 서비스에서 전역적으로 오래된 값의 지속 시간이 제한됨을 입증하지 않습니다. 동시 읽기 도구가 쓰기 전에 오래된 원본 값을 가져오고 무효화 뒤에 채울 수도 있습니다. 이 과정은 그 경합이나 동시 미스의 쇄도를 해결하지 않습니다.
원본 커밋과 Redis 삭제는 별도 작업입니다. 성공한 커밋 뒤 Redis가 삭제를 거부하면 기본 데이터베이스는 이미 바뀌었습니다. 오류는 캐시 작업 실패로 전파되어야 하며 기본 데이터베이스 롤백이나 완료된 무효화라고 설명해서는 안 됩니다. 격리 채점기는 실제 서비스를 고장 내지 않고 그 Redis 자체 실패를 시험합니다. 운영 재시도, 영속적 무효화, 장애 복구는 이 실습 밖의 추가 설계가 필요합니다.
요약
실제 기본 데이터베이스 커밋과 정확한 캐시 무효화를 연결하고, 거부된 원본 쓰기 후 캐시 데이터를 보존하며, 자연 만료로 외부 변경을 관찰했습니다. 정책은 원본 쓰기 실패와 그 뒤의 Redis 실패를 구분하고 순차 실행에서의 일관성 경계를 명시합니다.



