Hacker DirectoryHacker Directory

WEB / WEB-18.03

NoSQL and key-value stores

Distinguish storage models in the NoSQL family; read a public note key/value, version, and lifetime contract in a selected local adapter.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Value found

  1. Public read intent

    The application prepares a read for public:note:tea. This example’s store is authoritative only for its own memory state; the key prefix establishes neither identity nor permission.

    Application
    Public note read requested
    Local adapter
    No result
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  2. Key mapping

    The adapter uses the selected public key; its prefix implements neither authorization nor data validation.

    Application
    Public note read requested
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  3. Lifetime decision

    Selected logical clock=49 is below expiresAt=50. The store finds the value; that lookup outcome is not yet the application’s JSON-shape decision.

    Application
    Public note read requested
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Clock=49 < expiry=50; value found
    Result decision
    No application decision
  4. Lookup result

    The adapter returns found with a JSON string for public:note:tea containing id=tea, title=Tea note and version=1. Parsing and shape validation are the next local step.

    Application
    Public note read requested
    Local adapter
    Found JSON string
    Selected KV owner
    Clock=49 < expiry=50; value found
    Result decision
    No application decision
  5. Result processing

    The application parses the JSON string, validates the id/title/version shape and creates an independent copy. Changing that copy’s fields sends no store-write operation.

    Application
    Public note read requested
    Local adapter
    Found JSON string
    Selected KV owner
    Clock=49 < expiry=50; value found
    Result decision
    Parsed copy; shape valid
  6. Application choice

    The application uses its selected result; persistence, replica visibility, and permissions do not follow from this lookup.

    Application
    Tea note; version=1
    Local adapter
    Found JSON string
    Selected KV owner
    Clock=49 < expiry=50; value found
    Result decision
    Parsed copy; shape valid

Lifetime ended

  1. Public read intent

    The application prepares a read for public:note:tea. This example’s store is authoritative only for its own memory state; the key prefix establishes neither identity nor permission.

    Application
    Public note read requested
    Local adapter
    No result
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  2. Key mapping

    The adapter uses the selected public key; its prefix implements neither authorization nor data validation.

    Application
    Public note read requested
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  3. Lifetime decision

    Logical clock=50 reaches expiresAt=50; equality produces missing in this profile. The store retains no value for lookup, but the application has not received the missing result yet.

    Application
    Public note read requested
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Clock=50 reaches expiry=50; missing
    Result decision
    No application decision
  4. Lookup result

    The adapter returns missing with fixtureReason=expired in this teaching record. A general product API is not assumed to know this cause for every miss; there is no JSON value.

    Application
    Public note read requested
    Local adapter
    Missing; fixture reason expired
    Selected KV owner
    Clock=50 reaches expiry=50; missing
    Result decision
    No application decision
  5. Result processing

    Missing provides no note to parse and this branch creates no default note. A cache fallback read from another authoritative owner requires a separate contract and is not performed in this scene.

    Application
    Public note read requested
    Local adapter
    Missing; fixture reason expired
    Selected KV owner
    Clock=50 reaches expiry=50; missing
    Result decision
    No parsed note; no default
  6. Application choice

    The application uses its selected result; persistence, replica visibility, and permissions do not follow from this lookup.

    Application
    Missing received; no note; no fallback shown
    Local adapter
    Missing; fixture reason expired
    Selected KV owner
    Clock=50 reaches expiry=50; missing
    Result decision
    No parsed note; no default

Stale revision

  1. Conditional update ready

    The application carries expectedVersion=1 from an earlier read and requests a conditional update to public:note:tea. That old expectation does not establish the current store version; no update is accepted yet.

    Application
    Conditional update ready; expectedVersion=1
    Local adapter
    No result
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  2. Key mapping

    The adapter uses the selected public key; its prefix implements neither authorization nor data validation.

    Application
    Public note read requested
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  3. New revision

    An intervening update has produced id=tea, title=Coffee note and version=2 in the store. The application retains expectedVersion=1; its earlier read does not make that current owner state known.

    Application
    Expected version 1
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Current Coffee note; version=2
    Result decision
    No application decision
  4. Condition check

    In one serialized decision, the owner compares expectedVersion=1 with currentVersion=2 and finds a mismatch. This local conditional-update decision is not a distributed lock or collection transaction.

    Application
    Expected version 1
    Local adapter
    Compare expected=1/current=2
    Selected KV owner
    Current Coffee note; version=2
    Result decision
    No application decision
  5. Reject stale

    The mismatch withholds the update, preserving afterValue as Coffee note/version=2. A separate read can compare that final value; rejection alone does not mean an entire transaction was rolled back.

    Application
    Expected version 1
    Local adapter
    Compare expected=1/current=2
    Selected KV owner
    Coffee note; version=2 preserved
    Result decision
    No application decision
  6. Conflict result

    The application sees that its stale decision was not applied; this does not test a product whole-transaction rollback.

    Application
    Update denied; expectedVersion=1
    Local adapter
    Compare expected=1/current=2
    Selected KV owner
    Coffee note; version=2 preserved
    Result decision
    Conflict; no overwrite

Store unavailable

  1. Public read intent

    The application prepares a read for public:note:tea. This example’s store is authoritative only for its own memory state; the key prefix establishes neither identity nor permission.

    Application
    Public note read requested
    Local adapter
    No result
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  2. Key mapping

    The adapter uses the selected public key; its prefix implements neither authorization nor data validation.

    Application
    Public note read requested
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Stored JSON; selected lifetime
    Result decision
    No application decision
  3. Unavailable

    The selected fixture injects availability=false; this does not establish that the note is absent or expired.

    Application
    Public note read requested
    Local adapter
    Key selected: public:note:tea
    Selected KV owner
    Injected availability=false
    Result decision
    No application decision
  4. Separate error

    The adapter returns an unavailable error; this contract contains no fallback collapsing it into missing.

    Application
    Public note read requested
    Local adapter
    Error; no value/miss
    Selected KV owner
    Injected availability=false
    Result decision
    No application decision
  5. Stop decision

    The application creates neither authoritative empty state nor a default note; only the bounded failure outcome is shown.

    Application
    Public note read requested
    Local adapter
    Error; no value/miss
    Selected KV owner
    Injected availability=false
    Result decision
    Stop decision; no default

ACTORS · STATE · DATA FLOW

NoSQL is not one database behavior

NoSQL groups several data models outside relational tables; documents and key-value storage are our two examples. One product query language, validation, transactions, and consistency do not automatically apply to another. MongoDB collections can contain documents with different fields, yet the application still needs meaningful field contracts. MongoDB supports schema validation; embedding or referencing depends on access patterns and relationship ownership.

In a key-value model the application selects a key and the store returns its value. public:note:tea is a lookup name for a public synthetic note; its prefix does not implement authentication or authorization. Our local adapter stores a JSON string, which the application parses and validates as id/title/version. Receiving a string does not prove that its JSON has the expected shape. Redis GET distinguishes an existing string, a missing key, and a wrong-type error; this product contract is not universal NoSQL behavior.

Choose a model by identifying fields read together, changes that must be atomic together, and the authoritative owner. MongoDB single-document writes are atomic; an operation changing many documents is not automatically atomic as a whole. Multi-document transactions also exist. Redis processes an EXEC group serially, but does not provide SQL-style rollback for command errors. Our native adapter executes neither product: it illustrates serialized access within one selected process.

Lifetime, revision, and availability differ

The fixture assigns expiresAt=50 in logical clock units. At 49 the value exists; at 50 it becomes missing at the expiry boundary. This is not a wall-clock timer test. Eviction removes keys before expiry according to memory pressure and policy. Redis maxmemory policies can evict keys, while noeviction can instead reject new writes; not every policy deletes data. A key might never have existed either. A plain missing result cannot establish its cause.

TTL is a store lifecycle contract, not an arbitrary document field. Ordinary Redis SET replacement clears an existing TTL; retention needs an explicit option such as KEEPTTL. EXPIRE establishes a timeout. Replacement and explicit eviction are separate local fixture events. GET success is not a persistence guarantee: Redis RDB, AOF, and disabled persistence have different loss boundaries. Our memory-only adapter stores nothing beyond its process and claims neither restart survival nor crash durability.

An application that read version 1 may encounter owner version 2 before updating. The selected adapter compares expected and current revisions in one serialized decision and changes nothing on mismatch. This local contract does not test distributed atomicity; Redis WATCH/EXEC has its own optimistic condition semantics. An unavailable store raises a separate error: converting it into a missing key and empty authoritative state changes application decisions incorrectly. Replica visibility, consistency levels, and retries require a separately selected product and topology.

2. Red · Trust & Impact

Read every scenario without motion

Version stale

  1. The KV owner and encoding were selected

    public:note:tea is the key for a public note stored as a JSON string. The prefix is data mapping, not read or write permission for an actor.

    KV operation context
    Key: public:note:tea
    Owner/version gate
    Version check pending
    Application state boundary
    State unchanged
  2. Expected and current versions were separated

    The stale record has expectedVersion=1 and currentVersion=2. An old caller expectation does not make the current Coffee note in the store non-authoritative.

    KV operation context
    Expected=1
    Owner/version gate
    Current=2
    Application state boundary
    Decision pending
  3. The serialized-owner gate rejected the update

    accepted=false is a selected single-owner decision. It compares the current version and withholds the stale update without claiming every separate read/write is atomic.

    KV operation context
    Version mismatch known
    Owner/version gate
    Accepted=false
    Application state boundary
    Update withheld
  4. The after-value was preserved

    afterValue retains id=tea, title=Coffee note and version=2. The negative fixture shows preservation of current data, not only a denial label.

    KV operation context
    After: Coffee note
    Owner/version gate
    After version=2
    Application state boundary
    No stale overwrite
  5. Product atomicity was not expanded incorrectly

    MongoDB single-document atomicity does not automatically make a whole updateMany atomic. Redis EXEC is not the same contract as SQL rollback; operation scope remains explicit.

    KV operation context
    Single-owner scope
    Owner/version gate
    Product scopes differ
    Application state boundary
    No collection-wide claim
  6. No persistence outcome was added

    selectedModel uses persistence=none. State preservation is known for the memory adapter, not replica visibility, disk durability or crash recovery.

    KV operation context
    Memory state preserved
    Owner/version gate
    Persistence=none
    Application state boundary
    Durability unestablished

Unavailable

  1. The unavailable record was selected

    unavailable.status=error. This is not the same contract result as a lookup missing through expiry or eviction.

    KV operation context
    Status: error
    Owner/version gate
    Fallback decision pending
    Application state boundary
    Default not created
  2. Conversion to missing was rejected

    treatedAsMissing=false remains. Treating unavailable owner state as an empty note can create incorrect new state while authority is unknown.

    KV operation context
    Error ≠ missing
    Owner/version gate
    Authoritative value UNKNOWN
    Application state boundary
    No guessed empty note
  3. The default-creation boundary stayed closed

    createdDefaultNote=false. An adapter error is not routed into a successfully observed missing path, so state-changing fallback does not begin.

    KV operation context
    Unavailable known
    Owner/version gate
    Default creation=false
    Application state boundary
    No state-changing fallback
  4. Cache and authority were separated

    A memory-cache miss may need a read from another owner. This fixture says authoritativeForFixtureOnly=true without defining a real database fallback owner.

    KV operation context
    Fixture owner only
    Owner/version gate
    External fallback unspecified
    Application state boundary
    No invented source value
  5. A success reply did not become persistence

    Even successful Redis GET does not prove persistence. An error here supplies still less state evidence; outage, missing data and durability are separate questions.

    KV operation context
    State unknown on error
    Owner/version gate
    Durability separate
    Application state boundary
    No clean default state
  6. The safe result was reported correctly

    The known result is no conversion of error to missing and no default-note creation. This branch cannot establish whether data exists, expired or was deleted.

    KV operation context
    No false missing
    Owner/version gate
    No default mutation
    Application state boundary
    Data existence UNKNOWN

ASSUMPTION · BOUNDARY · CONTROL

A missing result does not explain ownership

When a controlled public-note lookup returns missing, identify the owner and the errors the adapter distinguishes. An expired cache entry and an authoritative record never written can require different product decisions. A memory-only cache miss may need a database fallback; collapsing an authoritative-store outage into missing can produce false default state. These scenarios use only authored logical lifetime and an injected unavailable event, not Redis telemetry.

Key naming organizes data, while a public prefix alone grants no permission. Validating id, title and version against the expected JSON shape is separate. Treating store success as sufficient application semantics can lead to decisions over the wrong note or revision. Field/schema checks and data ownership are reviewed together rather than inferred from a string lookup result.

Attach atomicity to an operation boundary

A local expected-revision rejection proves only one serialized owner decision. Excluding a state change between separate read and write operations requires another assumption. Extending MongoDB single-document atomicity to a collection, or Redis serialized EXEC processing to SQL rollback, is likewise incorrect. The impact can be partially changed related data or a stale decision overwriting newer state. Our harmless version mismatch preserves the selected state.

Do not equate an accepted reply with persistence or replica visibility. An update accepted in process memory can be lost after a crash; TTL, eviction, and persistence policies are different mechanisms. Review expects an explicit operation scope, failure outcome, and application fallback decision. This fixture measures no cluster, replication lag, concurrent clients, or durability; drawing those capabilities into the model would also create false evidence.

3. Blue · Secure development & defense

Read every scenario without motion

TTL and eviction

  1. The logical-clock boundary was read

    At hit.clock=49 and expiresAt=50, the value is found. This logical-clock result is not a production Redis timing or eviction-policy measurement.

    Value/lifetime observation
    Clock=49
    Adapter contract review
    Expires at=50
    Evidence conclusion
    Status=found
  2. The expiry-equality outcome was matched

    At expiry.clock=50, status=missing with fixtureReason=expired. The boundary differs from the normal hit; equality is not read as still found.

    Value/lifetime observation
    Clock=50
    Adapter contract review
    Status=missing
    Evidence conclusion
    Reason=expired
  3. Pre-expiry eviction was kept separate

    At eviction.clock=49, status=missing. The reason is an explicit authored eviction event, so it differs from expiry at clock=50.

    Value/lifetime observation
    Eviction at=49
    Adapter contract review
    Missing before expiry
    Evidence conclusion
    Cause differs
  4. Runtime reasons were not generalized

    fixtureReason is this example’s observation field. A real adapter need not distinguish expiry, eviction and never-written causes for every missing result.

    Value/lifetime observation
    Fixture cause known
    Adapter contract review
    Product cause may be unknown
    Evidence conclusion
    No invented telemetry
  5. The replacement-TTL contract was read

    replacement has oldExpiresAt=50, newExpiresAt=null and ttlPreserved=false. This replacement choice clears the TTL; preserving the old TTL is not assumed.

    Value/lifetime observation
    Old TTL boundary=50
    Adapter contract review
    New expiry=null
    Evidence conclusion
    TTL preserved=false
  6. Lifetime and persistence were separated

    Expiry, eviction and replacement are different lifetime mechanisms. None alone proves disk restore, replication or data survival after a process crash.

    Value/lifetime observation
    Lifetime rules known
    Adapter contract review
    Persistence separate
    Evidence conclusion
    No restore guarantee

Copy and selector

  1. The found-value encoding was reviewed

    The store keeps a JSON string; hit.value is parsed data with id=tea, title=Tea note and version=1. Store lookup and shape validation are different operations.

    Value/lifetime observation
    Stored: JSON string
    Adapter contract review
    Returned: parsed data
    Evidence conclusion
    Copy review pending
  2. The application-copy boundary was read

    applicationMutationChangesStore=false declares that the returned object is separate from store state. Changing a local field does not automatically update the store.

    Value/lifetime observation
    Returned copy independent
    Adapter contract review
    Store mutation=false
    Evidence conclusion
    No shared-object assumption
  3. The error-selector condition was defined

    The selector counts observations with status=error and treatedAsMissing=true. The normal error record has treatedAsMissing=false, giving expected=0.

    Value/lifetime observation
    Error/missing conversion condition
    Adapter contract review
    Normal expected=0
    Evidence conclusion
    Negative policy retained
  4. The positive comparison was bounded

    positiveExpected=1 is only an inconsistent observation comparison. It is not reported as an actual product event converting error into missing.

    Value/lifetime observation
    Comparison expected=1
    Adapter contract review
    Fixture only
    Evidence conclusion
    No actual incident claim
  5. The missing-observation outcome was separated

    With a missing record or treatedAsMissing field, count=0 cannot prove correct error handling. Adapter outcome and application fallback need coverage.

    Value/lifetime observation
    Coverage may be missing
    Adapter contract review
    Handling UNKNOWN
    Evidence conclusion
    No clean selector conclusion
  6. Copy, policy and product scope were separated

    The parsed copy and finite decision belong to the memory adapter, with productsExecuted=[]. Actual Redis/MongoDB behavior and namespace authorization need separate evidence.

    Value/lifetime observation
    Local copy behavior known
    Adapter contract review
    Products not executed
    Evidence conclusion
    Permission separate

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Connect adapter contracts to application decisions

Define found/value, missing, and unavailable/error outcomes at the application boundary. Validate the value schema after the store operation as a separate local step. The fixture returns a parsed copy so application mutation cannot silently change stored data. Key mapping is selected only for a public synthetic note. Real identity, authorization, namespace isolation, and presentation require separate design.

If missing triggers a fallback, state which owner is read and what is authoritative on errors. Here unavailable does not create an empty note: a state-changing decision stops. The contract does not authorize treating a cache as a database. Separate expiry and eviction observations help debugging, but actual adapters may not know every cause. Our missing-reason field is authored fixture observability, not a runtime field supplied by every Redis client.

Verify lifecycle policy independently

Use a selected logical clock to check immediately before and at expiry; test explicit pre-expiry eviction separately for the same value. Record whether replacement preserves or clears lifetime. Production Redis policies depend on current documentation and workload; this lesson recommends no memory-limit or fsync tuning value. Do not report the native Python adapter as Redis or MongoDB execution.

Independently read the current revision and verify preservation after a stale update. The local operation is one owner decision; other products have their own conditional-write, transaction and consistency contracts. If persistence is required, consider process memory, snapshots, append-only logs and restore separately. An accepted memory update does not imply preserved data after a crash or visibility at a replica.

NOW APPLY IT TO THE CASE

Why are missing, unavailable and stale versions different decisions?

In the public-note memory adapter, errors create no default note and stale versions preserve current state. TTL, eviction, replacement and parsed copies are reviewed through separate fields.

Compare the cached case representation with its authoritative data owner. Keep stored value, revision, transaction and cache-copy completion separate.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Public note key/version/lifetime ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "selectedModel": {
    "runtime": "own Python in-memory adapter, serialized owner access",
    "productsExecuted": [],
    "key": "public:note:tea",
    "valueEncoding": "JSON string",
    "authoritativeForFixtureOnly": true,
    "persistence": "none"
  },
  "hit": {
    "clock": 49,
    "expiresAt": 50,
    "status": "found",
    "value": {
      "id": "tea",
      "title": "Tea note",
      "version": 1
    },
    "applicationMutationChangesStore": false
  },
  "expiry": {
    "clock": 50,
    "status": "missing",
    "fixtureReason": "expired"
  },
  "eviction": {
    "clock": 49,
    "status": "missing",
    "fixtureReason": "explicit authored eviction event, not measured Redis policy"
  },
  "stale": {
    "expectedVersion": 1,
    "currentVersion": 2,
    "accepted": false,
    "afterValue": {
      "id": "tea",
      "title": "Coffee note",
      "version": 2
    },
    "decisionScope": "selected serialized single owner"
  },
  "unavailable": {
    "status": "error",
    "treatedAsMissing": false,
    "createdDefaultNote": false
  },
  "replacement": {
    "oldExpiresAt": 50,
    "newExpiresAt": null,
    "ttlPreserved": false,
    "contract": "selected replacement clears TTL; Redis SET reference only"
  },
  "productScopes": {
    "mongoSingleDocumentAtomic": true,
    "mongoUpdateManyWholeAutomaticallyAtomic": false,
    "redisExecIsSqlRollback": false,
    "redisGetSuccessProvesPersistence": false
  },
  "selector": {
    "language": "XPath",
    "xml": "<audit><kv status=\"error\" treatedAsMissing=\"false\"/></audit>",
    "xpath": "count(/audit/kv[@status='error' and @treatedAsMissing='true'])",
    "expected": 0,
    "positiveFixtureXml": "<audit><kv status=\"error\" treatedAsMissing=\"true\"/></audit>",
    "positiveExpected": 1,
    "scope": "Only authored protocol-decision observations, not product log field names or a live HTTP capture.",
    "missingFieldBehavior": "Missing observation is insufficient evidence; zero matches is not proof of correct runtime behavior."
  }
}
selectedModel
Local adapter scope, not product runtime.
hit/expiry/eviction
Logical clock and different missing conditions.
stale
Expected revision and preserved current value.
unavailable/replacement
Error semantics and selected lifetime replacement contract.
selector
Inert XPath counting synthetic missing/error decisions.

Public synthetic note; the own Python adapter is memory-only. No Redis/MongoDB, cluster, timer, or durability execution is claimed. Missing reason is a fixture field.

Two reality checks

NoSQL needs no schema or transactions.

Every model needs a data contract; selected products can provide validation and different atomicity/transaction scopes.

A missing key proves an available store and empty authoritative state.

Missing has multiple causes; unavailable is a separate error. Cache absence does not prove an absent authoritative record.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

The adapter returned an error. Should it be treated as missing and create a default note?

  1. Yes; every unsuccessful GET proves the key is absent.
  2. No; treatedAsMissing=false and no default creation.
Read the explained answer

No; treatedAsMissing=false and no default creation. — An error does not reveal the data owner state; silently converting it to a missing branch is incorrect.

Does the selected replacement preserve TTL, and was actual Redis executed?

Selected lesson artifact fields; teaching example

selectedModel
{"runtime":"own Python in-memory adapter, serialized owner access","productsExecuted":[],"key":"public:note:tea","valueEncoding":"JSON string","authoritativeForFixtureOnly":true,"persistence":"none"}
replacement
{"oldExpiresAt":50,"newExpiresAt":null,"ttlPreserved":false,"contract":"selected replacement clears TTL; Redis SET reference only"}
  1. TTL is cleared; productsExecuted is empty.
  2. Successful Redis GET guarantees persistence and preserved TTL.
Read the explained answer

TTL is cleared; productsExecuted is empty. — NewExpiresAt null/ttlPreserved=false is the selected rule; an owned memory adapter is not runtime evidence of the referenced product.

Why should Mongo single-document atomicity, updateMany and Redis EXEC not be equated with SQL-style rollback?

Read the explained answer

Single-document update scope does not make an entire updateMany operation automatically atomic. Redis EXEC is not equivalent to SQL rollback semantics. The selected stale-version rejection is a serialized-owner model; no actual Redis/Mongo deployment or distributed atomicity was tested.

□ I tied atomicity scope to the operation/product.

□ I did not count the memory adapter as a real-product test.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope