Test Details Overview

Keep failed LMDB opens retryable and stop backend copies from losing short all-zero keys (#5267)

* Keep a failed LMDB open from leaving its environment behind

backend_lmdb::open_impl stored the new environment in the backend
before it opened the schema's tables. When a required table was
missing, such as on a read-only open of a database that lacks it,
open_table threw and the environment stayed assigned while the
backend reported itself closed. Retrying the open on the same backend
then aborted on the "environment already open" assert.

The environment now stays local until every table has opened and the
opening transaction has committed, and is adopted together with the
table handles. A failed open closes it on unwinding, after aborting
the transaction, and leaves the backend closed and retryable, as the
RocksDB backend already does.

* Start the first parallel traversal partition at the table's beginning

backend::for_each_par split a table into partitions at 32 byte keys
whose first byte is the partition boundary, and the first partition
started at a lower bound of 32 zero bytes. Keys sort bytewise with a
shorter prefix first, so an all-zero key shorter than 32 bytes sorts
below that bound and no partition visited it. copy_to traverses every
table this way, so a migration dropped such keys without an error:
an online weight record at timestamp 0, whose key is 8 zero bytes,
never reached the destination.

The first partition now starts at begin (), the way the last one
already ends at end (). The boundaries between partitions were not
affected: a short key just below one still sorts above the previous
one and lands in the partition before it.

* Fail copy_to when a table copies fewer entries than its source holds

copy_to counted each source table up front, but used the count only
for progress reports. When the traversal visited fewer entries, as it
did for all-zero keys shorter than 32 bytes, the copy finished
without an error and the destination stayed short, so the LMDB to
RocksDB migration reported success over lost records.

After each table, copy_to now compares the entries it copied with the
source count and throws when they differ, naming the table and both
numbers. Tables whose backend only estimates its count, the RocksDB
pruned and final vote tables, are not checked; an LMDB source, the
one the migration copies from, counts every table exactly.

The test makes the traversal miss a key through a new before_count
hook on hooked_backend: it deletes a source key after copy_to opened
the transaction it counts in, so the key is counted but no longer
traversed. Without the check the copy completes on both backends.

__________

Type: commit

Hash: fb528ae4e8bd810c852937205f1acea59a6af44f

Created: today

__________

gr0v1ty/nano-node:fb528ae4e8bd810c852937205f1acea59a6af44f

Test Results

Test Case Status Duration Median Deviation Log Graph
5n4pr_conf_10k_bintree PASS 122 s 10.0 s GitHub job logs Show
5n4pr_conf_10k_change PASS 124 s -11.0 s GitHub job logs Show
5n4pr_conf_change_dependant PASS 127 s 10.0 s GitHub job logs -
5n4pr_conf_change_independant PASS 108 s -4.0 s GitHub job logs -
5n4pr_conf_send_dependant PASS 120 s 5.0 s GitHub job logs -
5n4pr_conf_send_independant PASS 132 s 19.0 s GitHub job logs -
5n4pr_rocks_10k_bintree PASS 110 s -2.0 s GitHub job logs -
5n4pr_rocks_10k_change PASS 436 s 278.0 s GitHub job logs -