Loading the parity record
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Parity / sled
Every number on this page was produced by running both implementations over the same cases: the real sled package pinned at 0.34.7 answers first, and its answer is the expectation the Go port is held to. Nothing is a hand-written expectation, so a new upstream release re-scores the port on its own. See sled for the port's own documentation. Source: github.com/malcolmston/sled.
Not a generic diagram: every node below names an artefact of this harness — the pinned package it installed, the runner files it started, the case files it streamed, and the counts it wrote out.
| Case group | Cases | Match | Mismatch | Group parity |
|---|---|---|---|---|
| durability | 12 | 11 | 1 | 91.7% |
| trees | 11 | 10 | 0 | 90.9% |
| core | 9 | 8 | 0 | 88.9% |
| iteration | 9 | 7 | 0 | 77.8% |
| merge | 9 | 9 | 0 | 100.0% |
| ends | 8 | 8 | 0 | 100.0% |
| batch | 7 | 6 | 0 | 85.7% |
| cas | 5 | 5 | 0 | 100.0% |
Every exported symbol of the upstream package, and what the port offers for it. The upstream list is derived mechanically, never from a README: S=~/.cargo/registry/src/index.crates.io-*/sled-0.34.7/src
every public function on the public types
for f in db tree batch config iter ivec result subscriber transaction; do
echo "-- $f.rs"
grep -n '^\s*pub \(async \)\?fn \|^pub struct \|^pub enum \|^pub trait ' "$S/$f.rs"
done
the public re-export surface of the crate root
sed -n '/^pub use self::{/,/^};/p' "$S/lib.rs"
the Config builder knobs, which are macro-generated
sed -n '/^ builder!(/,/^ );/p' "$S/config.rs". A symbol with no case is untested, never a match.
| Upstream symbol | Go symbol | Status | Cases | Note |
|---|---|---|---|---|
| sled::open | sled.Open | differs | all 70 | upstream takes a directory, the port takes the log file path |
| sled::Db::open | sled.Open | differs | all 70 | same divergence |
| sled::Config::new | sled.DefaultConfig | untested | — | the Rust runner opens through Config, the Go runner through Open; the builder itself is not compared |
| sled::Config::path | Config.Path | untested | — | directory vs file path (see divergence 1) |
| sled::Config::open | Config.Open | untested | — | |
| sled::Config::get_path | DB.Path | untested | — | |
| sled::Config::temporary | Config.Temporary / WithTemporary | untested | — | port deletes the log file on clean Close |
| sled::Config::cache_capacity | — | missing | — | port has no page cache (whole index is in memory) |
| sled::Config::mode (Mode::LowSpace/HighThroughput) | — | missing | — | no space/throughput trade-off knob |
| sled::Config::use_compression | — | missing | — | port does not compress |
| sled::Config::compression_factor | — | missing | — | |
| sled::Config::create_new | — | missing | — | port always creates-or-opens |
| sled::Config::flush_every_ms | — | missing | — | port has WithSyncWrites (per-commit fsync) instead |
| sled::Config::idgen_persist_interval | — | missing | — | port's id block size is not configurable |
| sled::Config::print_profile_on_drop | — | missing | — | |
| sled::Config::segment_size (#[doc(hidden)]) | — | missing | — | log-segment tuning has no analogue |
| sled::Config::snapshot_path | — | missing | — | deprecated no-op upstream |
| sled::Mode | — | missing | — | |
| Db::open_tree | DB.OpenTree | differs | multiple-trees-isolation, tree-name-and-default, opened-but-unwritten-tree-survives-reopen, emptied-tree-survives-reopen, cross-tree-batch-and-prefix | a tree that never received a write does not survive a restart in the port |
| Db::drop_tree | DB.DropTree | match | drop-tree, drop-missing-tree, drop-default-tree, drop-tree-then-reopen | both report false for a missing tree and both fail on the default tree |
| Db::tree_names | DB.TreeNames | match | tree-names-sorted, drop-tree, compact-preserves-nonempty, … | port returns []string, upstream Vec<IVec>; both hex-encoded by the runners |
| Db::was_recovered | DB.WasRecovered | match | was-recovered-fresh, reopen-twice | |
| Db::generate_id | DB.GenerateID | match | generate-id-monotonic, generate-id-monotonic-across-reopen | strictly increasing, including across restarts; absolute values are implementation-defined so only monotonicity is compared |
| Db::export | DB.Export | untested | — | byte format is implementation-specific and not comparable across languages |
| Db::import | DB.Import | untested | — | same |
| Db::checksum | DB.Checksum | untested | — | different digest construction; not comparable |
| Db::size_on_disk | DB.SizeOnDisk | untested | — | storage layouts are unrelated |
| Db::space_amplification | — | missing | — | |
| Db: Deref<Target = Tree> | DB.Set/Get/Delete/Scan/… | match | all core, cas, iteration cases | the default keyspace is reachable directly off the handle on both sides |
| Tree::insert | Tree.GetAndSet | match | insert-get-remove, insert-returns-previous-empty, non-utf8-keys, non-utf8-values, empty-value-roundtrip | both return the previous value, null when absent, and an empty string for a previously-empty value |
| Tree::set (deprecated alias of insert) | Tree.Set | differs | — | port's Set returns only an error and discards the previous value |
| Tree::get | Tree.Get | match | insert-get-remove, empty-value-roundtrip, … | present-with-empty-value is distinguished from absent on both sides |
| Tree::remove | Tree.GetAndDelete | match | insert-get-remove, remove-absent, removes-do-not-resurrect | returns the removed value; removing an absent key is not an error |
| Tree::del (deprecated alias of remove) | Tree.Delete | differs | — | port's Delete discards the previous value |
| Tree::compare_and_swap | Tree.CompareAndSwapErr | match | cas-sequence, cas-delete, cas-empty-vs-absent, cas-non-utf8, cas-survives-reopen | on failure both report current and proposed; old=None means expect-absent, new=None means delete |
| Tree::update_and_fetch | Tree.UpdateAndFetch | match | update-and-fetch, update-and-fetch-delete | returns the new value; None/nil deletes |
| Tree::fetch_and_update | Tree.FetchAndUpdate | match | fetch-and-update, update-and-fetch-delete | returns the previous value |
| Tree::apply_batch | Tree.ApplyBatch | match | batch-mixed, batch-same-key-twice, batch-atomic-across-reopen, batch-named-tree, batch-remove-absent, batch-empty | applied atomically; later ops in a batch win; survives a reopen |
| Tree::merge | Tree.Merge | match | merge-without-operator, merge-concat, merge-sum-u64, merge-delete, merge-per-tree-operator, merge-survives-reopen | both fail when no operator is installed; both delete on a nil/None result |
| Tree::set_merge_operator | Tree.SetMergeOperator | match | merge-* | per-tree, effective immediately, lost on close (both runners reinstall after reopen) |
| Tree::iter | Tree.Scan(sled.Range{}) | match | iter-forward-reverse, iter-empty-tree, iter-after-removes, iter-survives-reopen | ascending unsigned-byte key order |
| Tree::range | Tree.Scan(sled.Range{Lower,Upper}) | differs | range-lower-inclusive-upper-exclusive, range-non-utf8-bounds, range-exclusive-lower, range-inclusive-upper | inclusive-lower/exclusive-upper agrees exactly; the port cannot express an exclusive lower or inclusive upper bound |
| Tree::scan_prefix | Tree.ScanPrefix / sled.PrefixRange | match | prefix-forward-reverse, cross-tree-batch-and-prefix, iter-survives-reopen | empty prefix scans everything; 0xff prefix behaves identically |
| Iter: DoubleEndedIterator (.rev()) | sled.Range.Reverse | match | iter-forward-reverse, prefix-forward-reverse, range-lower-inclusive-upper-exclusive | reverse iteration visits the same set in the opposite order; bounds are unchanged |
| Tree::first | Tree.First | match | first-last, pop-then-reopen | |
| Tree::last | Tree.Last | match | first-last, pop-then-reopen | |
| Tree::pop_min | Tree.PopMin | match | pop-min-max-drain, pop-empty-tree, pop-non-utf8, pop-then-reopen | returns null and leaves the tree untouched when empty |
| Tree::pop_max | Tree.PopMax | match | pop-min-max-drain, pop-empty-tree, pop-non-utf8 | |
| Tree::get_gt | Tree.GetGt | match | get-gt-lt | |
| Tree::get_lt | Tree.GetLt | match | get-gt-lt | |
| Tree::contains_key | Tree.ContainsKey | match | contains-key, empty-value-roundtrip, cas-delete | |
| Tree::len | Tree.Len | match | len-clear-is-empty, insert-get-remove, … | |
| Tree::is_empty | Tree.IsEmpty | match | len-clear-is-empty, remove-absent, empty-db-reopen | |
| Tree::clear | Tree.Clear | match | len-clear-is-empty, clear-then-reopen | a cleared tree stays cleared across a reopen |
| Tree::name | Tree.Name | differs | tree-name-and-default, tree-names-sorted | upstream returns IVec (bytes), the port a string; the runners hex-encode, and the *values* agree |
| Tree::flush | DB.Flush | match | flush-then-reopen, batch-atomic-across-reopen, iter-survives-reopen, … | upstream returns the number of bytes flushed, the port only an error; the flush *effect* is what is compared |
| Tree::flush_async | DB.FlushAsync | untested | — | async fn vs a <-chan error; not scriptable deterministically |
| Tree::watch_prefix | Tree.Watch | untested | — | event streams are excluded by design (nondeterministic to compare) |
| Tree::checksum | Tree.Checksum | untested | — | different digest construction |
| Tree::verify_integrity | — | missing | — | no analogue |
| Tree::transaction | DB.Update / DB.View | untested | — | closure-shaped transaction APIs with different conflict models; not encodable as a language-agnostic script here |
| sled::CompareAndSwapError{current, proposed} | sled.CompareAndSwapError{Current, Proposed} | match | cas-sequence, cas-non-utf8 | field-for-field |
| Batch::default | DB.NewBatch | match | all batch-* | |
| Batch::insert | Batch.Set | match | all batch-* | |
| Batch::remove | Batch.Delete | match | batch-mixed, batch-same-key-twice, batch-remove-absent | |
| Iter: Iterator | sled.Iterator (Valid/Key/Value/Next) | match | all iteration cases | |
| Iter::keys | — | missing | — | no keys-only iterator |
| Iter::values | — | missing | — | no values-only iterator |
| sled::IVec | []byte | differs | all | the port uses plain byte slices; no inline/Arc value type |
| IVec::subslice | — | missing | — | |
| sled::Error (CollectionNotFound, Unsupported, ReportableBug, Io, Corruption) | sled.Err* sentinels | untested | — | the harness compares *whether* a call failed, never message text (HARNESS rule); the sentinel sets do not correspond one-to-one |
| sled::Event (Insert/Remove) + Event::key | sled.Event / sled.EventType | untested | — | subscriber surface excluded |
| Subscriber: Iterator | Subscriber.Next / Subscriber.Events | untested | — | |
| Subscriber::next_timeout | — | missing | — | port has TryNext/Drain instead |
| Subscriber::complete | — | missing | — | no event-completion handshake |
| sled::Transactional (trait) | — | missing | — | port spans trees with a single DB.Update + Tx.SetTree instead of a tuple-of-trees trait |
| transaction::TransactionalTree::insert | Tx.Set / Tx.SetTree | untested | — | |
| transaction::TransactionalTree::remove | Tx.Delete / Tx.DeleteTree | untested | — | |
| transaction::TransactionalTree::get | Tx.Get / Tx.GetTree | untested | — | |
| transaction::TransactionalTree::apply_batch | — | missing | — | no batch-inside-transaction |
| transaction::TransactionalTree::flush | — | missing | — | port always commits durably |
| transaction::TransactionalTree::generate_id | — | missing | — | not available on Tx |
| transaction::abort | — | missing | — | port aborts by returning an error from the closure |
| transaction::TransactionError | — | missing | — | |
| transaction::ConflictableTransactionError | — | missing | — | |
| transaction::UnabortableTransactionError | — | missing | — | |
| — | DB.Compact | extra | compact-preserves-nonempty, compact-destroys-empty-tree, compact-after-many-overwrites | compared against a no-op upstream (Db::flush), since compaction must be semantically invisible; **one case fails — see the bug section |
| — | Tree.GetGte / Tree.GetLte | extra | get-gte-lte | compared against a range()-based emulation in the Rust runner |
| — | Tree.PopMinInRange / Tree.PopMaxInRange | extra | pop-in-range | compared against a range()+remove() emulation in the Rust runner |
| — | Tree.CompareAndSwap (bool form) | extra | — | CompareAndSwapErr is the upstream-shaped form and is the one scored |
| — | DB.GetAndSet / DB.GetAndDelete | extra | all core | value-returning aliases that restore the upstream insert/remove contract |
| — | Tree.Batch / DB.Batch / Batch.Len | extra | — | closure-scoped batch helpers |
| — | sled.Range / sled.PrefixRange | extra | all iteration | struct-shaped replacement for Rust RangeBounds; see divergence 3 |
| — | sled.Option, WithSyncWrites, WithFileMode, WithTemporary | extra | — | functional options replacing part of Config |
| — | sled.DefaultTreeName | extra | tree-name-and-default | exported constant for the __sled__default keyspace (upstream keeps it private) |
| — | DB.Update / DB.View / sled.Tx | extra | — | see the transaction rows above |
| — | Subscriber.Drain / TryNext / Events / Close | extra | — | subscriber surface excluded |
| — | DB.Clear, DB.IsEmpty, DB.Path, DB.Checksum, DB.SizeOnDisk | extra | len-clear-is-empty, empty-db-reopen | Db-level conveniences for the default tree (upstream reaches them through Deref) |
| — | sled.ErrClosed, ErrEmptyKey, ErrTxClosed, ErrTxNotWritable, ErrEmptyTreeName, ErrDropDefaultTree, ErrNoMergeOperator, ErrNilFunc, ErrCorruptImport, ErrNoPath | extra | empty-key, drop-default-tree, open-tree-empty-name, merge-without-operator | error identity is not compared, only failure vs success |
Every case the harness streamed to both runners, with the exact upstream symbol and Go symbol it exercised. A deliberate, documented difference is a deviation and is counted apart from a mismatch.
| Case | Group | Upstream symbol | Go symbol | Status | Note |
|---|---|---|---|---|---|
| batch-mixed | batch | Tree::apply_batch | Tree.ApplyBatch | match | |
| batch-same-key-twice | batch | Tree::apply_batch | Tree.ApplyBatch | match | later operations in a batch win |
| batch-atomic-across-reopen | batch | Tree::apply_batch | Tree.ApplyBatch | match | a committed batch is applied all-or-nothing and survives a reopen |
| batch-named-tree | batch | Tree::apply_batch | Tree.ApplyBatch | match | a batch applies to the tree it is applied against, not the default tree |
| batch-remove-absent | batch | Tree::apply_batch | Tree.ApplyBatch | match | |
| batch-empty-key | batch | Tree::apply_batch | Tree.ApplyBatch | deviation | Go port rejects empty keys (sled.ErrEmptyKey) and applies nothing |
| batch-empty | batch | Tree::apply_batch | Tree.ApplyBatch | match | |
| cas-sequence | cas | Tree::compare_and_swap | Tree.CompareAndSwapErr | match | a failed CAS reports the current and proposed values |
| cas-delete | cas | Tree::compare_and_swap | Tree.CompareAndSwapErr | match | new=null means delete; old=null means expect-absent |
| cas-empty-vs-absent | cas | Tree::compare_and_swap | Tree.CompareAndSwapErr | match | expect-absent must not match a present key holding an empty value |
| cas-non-utf8 | cas | Tree::compare_and_swap | Tree.CompareAndSwapErr | match | |
| cas-survives-reopen | cas | Tree::compare_and_swap | Tree.CompareAndSwapErr | match | |
| insert-get-remove | core | Tree::insert / Tree::get / Tree::remove | Tree.GetAndSet / Tree.Get / Tree.GetAndDelete | match | sled returns the PREVIOUS value from insert and remove; null when the key was absent |
| insert-returns-previous-empty | core | Tree::insert | Tree.GetAndSet | match | an existing empty value must come back as an empty string, not null |
| remove-absent | core | Tree::remove | Tree.GetAndDelete | match | |
| empty-value-roundtrip | core | Tree::get | Tree.Get | match | empty value is distinct from an absent key |
| non-utf8-keys | core | Tree::insert | Tree.GetAndSet | match | keys are opaque bytes ordered lexicographically by unsigned byte value |
| non-utf8-values | core | Tree::insert | Tree.GetAndSet | match | |
| empty-key | core | Tree::insert | Tree.GetAndSet | deviation | Go port documents keys must be non-empty (sled.ErrEmptyKey) |
| len-clear-is-empty | core | Tree::len / Tree::clear / Tree::is_empty | Tree.Len / Tree.Clear / Tree.IsEmpty | match | |
| contains-key | core | Tree::contains_key | Tree.ContainsKey | match | |
| was-recovered-fresh | durability | Db::was_recovered | DB.WasRecovered | match | |
| flush-then-reopen | durability | Tree::flush | DB.Flush | match | |
| reopen-without-explicit-flush | durability | Db::open | sled.Open | match | a normal close must make prior writes durable even without an explicit flush |
| reopen-twice | durability | Db::open | sled.Open | match | |
| clear-then-reopen | durability | Tree::clear | Tree.Clear | match | |
| removes-do-not-resurrect | durability | Tree::remove | Tree.GetAndDelete | match | deleted keys must not come back after a reopen |
| generate-id-monotonic | durability | Db::generate_id | DB.GenerateID | match | |
| generate-id-monotonic-across-reopen | durability | Db::generate_id | DB.GenerateID | match | ids must stay strictly increasing across restarts |
| compact-preserves-nonempty | durability | Db::flush (sled has no explicit compaction) | DB.Compact | match | compaction must be semantically invisible |
| compact-destroys-empty-tree | durability | Db::flush (sled has no explicit compaction) | DB.Compact | mismatch | KNOWN PORT BUG: Compact() followed by a reopen silently destroys a tree that is currently empty; a plain reopen (case emptied-tree-survives-reopen) preserves it |
| compact-after-many-overwrites | durability | Db::flush (sled has no explicit compaction) | DB.Compact | match | |
| empty-db-reopen | durability | Db::open | sled.Open | match | |
| first-last | ends | Tree::first / Tree::last | Tree.First / Tree.Last | match | |
| get-gt-lt | ends | Tree::get_gt / Tree::get_lt | Tree.GetGt / Tree.GetLt | match | |
| get-gte-lte | ends | Tree::range (emulated) | Tree.GetGte / Tree.GetLte | deviation | Go-only helpers, compared against a range()-based upstream emulation |
| pop-min-max-drain | ends | Tree::pop_min / Tree::pop_max | Tree.PopMin / Tree.PopMax | match | |
| pop-empty-tree | ends | Tree::pop_min / Tree::pop_max | Tree.PopMin / Tree.PopMax | match | |
| pop-non-utf8 | ends | Tree::pop_min / Tree::pop_max | Tree.PopMin / Tree.PopMax | match | |
| pop-in-range | ends | Tree::range + Tree::remove (emulated) | Tree.PopMinInRange / Tree.PopMaxInRange | deviation | Go-only helpers, compared against a range()+remove() upstream emulation |
| pop-then-reopen | ends | Tree::pop_min | Tree.PopMin | match | |
| iter-forward-reverse | iteration | Tree::iter | Tree.Scan | match | |
| iter-empty-tree | iteration | Tree::iter | Tree.Scan | match | |
| prefix-forward-reverse | iteration | Tree::scan_prefix | Tree.ScanPrefix | match | |
| range-lower-inclusive-upper-exclusive | iteration | Tree::range | Tree.Scan | match | Lower inclusive / Upper exclusive is the only form the Go port can express |
| range-exclusive-lower | iteration | Tree::range | Tree.Scan | deviation | Go port's Range.Lower is always inclusive |
| range-inclusive-upper | iteration | Tree::range | Tree.Scan | deviation | Go port's Range.Upper is always exclusive |
| range-non-utf8-bounds | iteration | Tree::range | Tree.Scan | match | |
| iter-after-removes | iteration | Tree::iter | Tree.Scan | match | |
| iter-survives-reopen | iteration | Tree::iter | Tree.Scan | match | |
| merge-without-operator | merge | Tree::merge | Tree.Merge | match | must fail on both sides: no merge operator installed |
| merge-concat | merge | Tree::merge / Tree::set_merge_operator | Tree.Merge / Tree.SetMergeOperator | match | the operator sees None/nil for an absent key |
| merge-sum-u64 | merge | Tree::merge | Tree.Merge | match | |
| merge-delete | merge | Tree::merge | Tree.Merge | match | an operator returning None deletes the key |
| merge-per-tree-operator | merge | Tree::set_merge_operator | Tree.SetMergeOperator | match | a merge operator is installed per tree |
| merge-survives-reopen | merge | Tree::merge | Tree.Merge | match | the runners reinstall the operator after reopen; the merged value must be durable |
| update-and-fetch | merge | Tree::update_and_fetch | Tree.UpdateAndFetch | match | returns the NEW value |
| fetch-and-update | merge | Tree::fetch_and_update | Tree.FetchAndUpdate | match | returns the PREVIOUS value |
| update-and-fetch-delete | merge | Tree::update_and_fetch | Tree.UpdateAndFetch | match | returning None from the closure deletes the key |
| multiple-trees-isolation | trees | Db::open_tree | DB.OpenTree | match | |
| tree-name-and-default | trees | Tree::name | Tree.Name | match | opening the default tree by name yields the same keyspace as the Db's own methods |
| drop-tree | trees | Db::drop_tree | DB.DropTree | match | |
| drop-missing-tree | trees | Db::drop_tree | DB.DropTree | match | dropping a tree that does not exist reports false, not an error |
| drop-default-tree | trees | Db::drop_tree | DB.DropTree | match | must fail on both sides: the default tree cannot be dropped |
| open-tree-empty-name | trees | Db::open_tree | DB.OpenTree | match | must fail on both sides |
| drop-tree-then-reopen | trees | Db::drop_tree | DB.DropTree | match | |
| tree-names-sorted | trees | Db::tree_names | DB.TreeNames | match | |
| opened-but-unwritten-tree-survives-reopen | trees | Db::open_tree | DB.OpenTree | deviation | Go port documents that an opened-but-never-written tree does not survive a restart |
| emptied-tree-survives-reopen | trees | Db::open_tree | DB.OpenTree | match | a tree that received a write and was then emptied still exists after a plain reopen |
| cross-tree-batch-and-prefix | trees | Db::open_tree | DB.OpenTree | match |