Skip to main content

CACHE_VERSION

Constant CACHE_VERSION 

Source
pub const CACHE_VERSION: u32 = 35;
Expand description

Cache version - increment when format changes.

v1: Initial release with string-based Decimal/NaiveDate v2: Binary Decimal (16 bytes) and NaiveDate (i32 days) v3: Fixed account type defaults in CachedOptions v4: Hash algorithm switched from SHA-256 to BLAKE3 — same 32-byte output so the header layout is unchanged, but old hashes won’t match new files. Bumping the version short-circuits stale caches at the header check instead of paying the rkyv deserialize cost only to fail the hash compare. v5: Transaction.postings: Vec<Posting> became Vec<Spanned<Posting>> (#1151). The inner posting bytes gained a Span + file_id per entry, so old cache files would rkyv-deserialize into the new type as junk. Header check forces a rebuild instead. v6: The #1163 newtype slices (#1169 Currency, #1171 Account, #1172 Tag, #1173 Link, #1174 MetaValue) swapped variant payload types from InternedStr/String to typed newtypes. The archived layout coincidentally matches AsInternedStr in most cases, but MetaValue::{Account,Currency,Tag,Link} and Transaction.tags/links (plus the parallel Document fields) changed their archive wrappers. Bumping the version forces regeneration so we don’t risk rkyv reading old bytes into a structurally-different ArchivedMetaValue. v7: PriceAnnotation refactored from 6-variant enum to { kind: PriceKind, amount: Option<IncompleteAmount> } (#1167). Old cache bytes for the enum’s discriminant would deserialize as nonsense in the new struct layout. v8: CostSpec.{number_per,number_total}: Option<Decimal> collapsed into CostSpec.number: Option<CostNumber> where CostNumber is a 3-variant enum (PerUnit, Total, PerUnitFromTotal) (#1164). The archived layout is structurally different (Option<Decimal> + Option<Decimal>Option<discriminant + payload>); reading v7 bytes into the v8 layout would produce garbage cost numbers. Bumping forces regeneration. Subsequent #1164 follow-up commits converted CostNumber’s variants from tuple form (PerUnit(Decimal)) to struct form (PerUnit { value: Decimal }) so serde could apply tag = "kind" for cross-boundary wire unification. The rkyv- archived layout for a single-field struct variant is byte- identical to the tuple variant (both pack Archived<Decimal> positionally) — verified against rkyv 0.8.16 — so this change does NOT require a separate version bump. If a future rkyv version changes that encoding, OR if CostNumber gains additional fields, bump CACHE_VERSION to the next value. v13: CostNumber gained the Compound { per_unit, total } variant (#1700) and the parser now emits it for {a # b} cost specs — exactly the “gains additional fields” case the v12 note calls out. Without the bump, a cache written by a pre-#1700 binary serves the old misparse (Total{b} / PerUnit{b}) to fixed binaries, resurrecting the bug for any previously-loaded ledger. v14: green compound-cost conversion now retries past unparsable pre-/post-hash NUMBER tokens like red (#1713); inputs with garbage around {a # b} parse to different CostNumber values than v13 cached them as. v15: EOF now terminates a line in the parser’s error-recovery walkers, so a malformed FINAL line without a trailing newline emits its diagnostic (#1884). Previously such a file parsed to zero errors and rledger check exited 0 on it. The cached errors differ, and a cache written by a pre-fix binary would serve the silent-pass result to a fixed one — resurrecting exactly the bug for every ledger already in the cache, which is the worst case since the symptom is “no error reported”. v16: a sign separated from its operand is no longer dropped. - 7.50 USD (valid beancount) parsed as +7.50, and -,123.00 USD (malformed) parsed as +123.00 with no diagnostic, because the sign landed outside the AMOUNT node where nothing read it. Cached postings from a pre-fix binary therefore hold the WRONG SIGN, and the malformed case also holds an empty errors list. Serving either to a fixed binary reproduces the original bug on every ledger already in the cache — and a flipped sign is silent, so nothing downstream would flag it. v17: balance / price values are no longer read as “the first NUMBER token”. Parenthesized arithmetic evaluated to the leading operand ((1 + 5) / 2.1 USD asserted against 1, not 2.857…), and a split numeral did the same (1,23,4.50 USD -> 1, a thousandfold error). Cached directives from a pre-fix binary therefore hold those wrong VALUES, and the malformed cases also hold an empty errors list — both silent, so nothing downstream would flag them. v9: CachedOptions gained a set_options: Vec<String> field (#1340). It was previously dropped, so a cache hit lost the record of which options the file explicitly set — making resolve_effective_booking_method re-book FIFO/LIFO ledgers as STRICT. The new trailing field changes the archived layout, so old bytes must be regenerated. v10: String literals are now escape-decoded at parse (\"->", etc.); the stored narration/payee/meta/etc. bytes differ from the old raw form, so a cache hit would serve stale, still-escaped strings. v11: MetaValue gained an Int(i64) variant (appended last). Integer metadata literals (key: 42) now archive as Int rather than Number, and the new discriminant changes the enum’s archived layout, so old bytes must be regenerated. v12: CachedOptions gained display_precision, use_precise_interpolation, and plugin_processing_mode — previously dropped, so a cache hit silently ignored option "display_precision" "USD:0.0001" (formatting fell back to inferred precision) and the other two settings. New fields change the archived layout, so old bytes must be regenerated. v18: arithmetic in a COST SPEC is now evaluated rather than truncated to its first operand (#1939). {10.00 * 3 USD} previously archived a cost of 10.00; it now archives 30.00. The layout is unchanged, so nothing here would REJECT the old bytes — which is exactly why the bump is required: a stale cache would keep serving the truncated cost basis, and the file would keep failing to balance, on a build that has the fix. Verified by hitting precisely that during development. v19: arithmetic is now evaluated in METADATA values and BALANCE TOLERANCES as well as cost specs (#1944). key: 2 * 3 archives 6 not 2, and ~ 0.005 * 2 archives 0.010 not 0.005. Values again, not layout — and again the reason the bump is mandatory: a stale cache would keep serving the truncated tolerance and keep rejecting a file the fixed build accepts. v20: account names accept any NON-ASCII character inside a component (#1930). A ledger that previously failed to parse now yields directives, and one that parsed may gain account names it did not have. Layout unchanged, so old bytes would be accepted and a cached PARSE FAILURE served on a build that can read the file. v21: a #tag / ^link on a directive that does not take one is now a parse error (#1949). A file that previously loaded clean can now carry errors, so a stale cache would serve the old clean parse on a build that objects. v22: a metadata key now needs at least two characters, as in beancount (#1955). A file using k: 42 previously loaded clean and now carries a parse error, so a stale cache would serve the old clean parse on a build that objects. v23: a ^link is no longer accepted as a metadata VALUE (#1954), so a file using ref: ^x moves from clean to erroring. A stale cache would serve the old clean parse. v24: tags and links are no longer accepted as custom / pushmeta values (#1958), so a file using them moves from clean to erroring and a stale cache would serve the old clean parse. v25: transaction headers beancount’s grammar rejects are now parse errors (#2008) - a third header string, a string after a tag/link, or junk after the narration. Same reasoning as v24: those files move from clean to erroring, and this was observed for real - the first rledger check run against the fixtures after the change reported only the old downstream E1001s, because the cache still held the permissive parse. v26: malformed cost-spec component lists are now parse errors (#2008 cases 1 and 2) - an empty comma-delimited component, or a token after a component is already complete. Same clean-to-erroring move as v25, and a separate version because a cache written between the two lands is stale for this change even though it carries v25. v27: a cost spec with no number the author wrote now archives number: None instead of an invented zero (#2008). { # CCY} used to become Compound { per_unit: 0, total: 0 }, and a malformed spec had a number scraped out of it. Both changed the ARCHIVED CostSpec, so a stale cache would serve the invented number to a build that no longer produces one.

v28: a literal -0.00 now parses to an UNSIGNED zero (matching beancount, whose parser yields Decimal('0.00')), where the green path’s bare -n previously archived a signed zero. That is parser OUTPUT, so a stale cache would keep serving -0.00 from a build that no longer produces one.

v29: Posting::cost and Posting::price are boxed. rkyv mirrors the in-memory layout, so ArchivedPosting changed shape — a v28 file read as v29 would interpret an inline CostSpec as a relative pointer. This is a layout change rather than a content change, so unlike the entries above nothing about the ledger’s meaning moved.

v30: options declared in an INCLUDED file are no longer applied unless they accumulate across includes (#2151). Options is part of the cached payload, so a v29 cache replays the old resolution and resurrects the behavior: a sub-ledger’s booking_method still changing lot selection, a sub-ledger’s inferred_tolerance_default still letting an unbalanced transaction pass. Caught exactly that way while testing the fix – the matrices kept diverging until the stale caches were cleared.

v31: Note gained tags and links (#2160). The parser always accepted them on a note header and threw them away; now it keeps them, so the archived layout has two more fields. Without the bump a cache written by an older binary deserializes into the new struct and every note comes back with no tags – the exact bug, resurrected from disk.

v32: a blank line before a note or document no longer eats its tags and links (#2160 review). Parser OUTPUT, not layout: the archived shape is v31’s, but a v31 cache holds the empty tag lists the old parser produced, and this build would serve them as fact.

v33: ~ ends a balance’s amount expression, so 0.25 + 0.75 ~ 0.01 USD parses instead of erroring; and a price now refuses a tolerance it used to accept and half-read (#2191). Parser OUTPUT in BOTH directions: a v32 cache holds the error for the balance and the truncated 1.10 for the price, and this build produces neither.

v34: a query carrying a tag or link is now diagnosed (#2194). The DIRECTIVE is unchanged – reject_tags_and_links records an error and conversion still emits the Query, as it does for the other seven directives that refuse tags. What moves is the error list, and that is enough: a file with a tagged query used to parse clean and was therefore cacheable, so a v33 blob exists for it, and replaying that blob skips the parse that would now complain. Measured rather than assumed: a pre-#2194 binary run on a tagged query reports 0 errors and writes a cache, and this build reading that same blob reports 0 errors WITHOUT this bump and 2 WITH it. The bump is what surfaces the diagnostic.

v35: a balance tolerance whose currency disagrees with the amount’s, or which carries a second juxtaposed number, is diagnosed instead of read in part and discarded in part (#2193). Like v34 the DIRECTIVE is unchanged and the error list is what moves — and like v34 that is enough, because such a file used to parse clean and was therefore cacheable, so replaying its v34 blob skips the parse that now complains.

Public so rustledger-wasm can pin its own cache version against this one. Both caches archive the same Vec<Directive>, so a parser change that alters PARSER OUTPUT has to bump both — and on #1942 only this one was bumped, which review caught rather than any test. See loader_cache_version_is_pinned in rustledger-wasm/src/cache.rs.