Skip to content

Commit 6f83884

Browse files
[release/5.x] Cherry pick: Don't throw error if the ledger directory exists but is empty on node start (#6859) (#6885)
Co-authored-by: Andrea Piccione <[email protected]>
1 parent 7c2cb4b commit 6f83884

File tree

4 files changed

+88
-2
lines changed

4 files changed

+88
-2
lines changed

src/host/main.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,12 @@ int main(int argc, char** argv)
206206
{
207207
if (config.command.type == StartType::Start)
208208
{
209-
if (files::exists(config.ledger.directory))
209+
if (
210+
files::exists(config.ledger.directory) &&
211+
!fs::is_empty(config.ledger.directory))
210212
{
211213
throw std::logic_error(fmt::format(
212-
"On start, ledger directory should not exist ({})",
214+
"On start, ledger directory should not exist or be empty ({})",
213215
config.ledger.directory));
214216
}
215217
// Count members with public encryption key as only these members will be

src/host/snapshots.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ namespace asynchost
158158
continue;
159159
}
160160

161+
if (fs::exists(f.path()) && fs::is_empty(f.path()))
162+
{
163+
LOG_INFO_FMT("Ignoring empty snapshot file {}", file_name);
164+
continue;
165+
}
166+
161167
auto snapshot_idx = get_snapshot_idx_from_file_name(file_name);
162168
if (snapshot_idx > latest_committed_snapshot_idx)
163169
{

tests/e2e_operations.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,40 @@ def test_large_snapshot(network, args):
205205
)
206206

207207

208+
def test_empty_snapshot(network, args):
209+
210+
LOG.info("Check that empty snapshot is ignored")
211+
212+
with tempfile.TemporaryDirectory() as snapshots_dir:
213+
LOG.debug(f"Using {snapshots_dir} as snapshots directory")
214+
215+
snapshot_name = "snapshot_1000_1500.committed"
216+
217+
with open(
218+
os.path.join(snapshots_dir, snapshot_name), "wb+"
219+
) as temp_empty_snapshot:
220+
221+
LOG.debug(f"Created empty snapshot {temp_empty_snapshot.name}")
222+
223+
# Check the file is indeed empty
224+
assert (
225+
os.stat(temp_empty_snapshot.name).st_size == 0
226+
), temp_empty_snapshot.name
227+
228+
# Create new node and join network
229+
new_node = network.create_node("local://localhost")
230+
network.join_node(new_node, args.package, args, snapshots_dir=snapshots_dir)
231+
new_node.stop()
232+
233+
# Check that the empty snapshot is correctly skipped
234+
if not new_node.check_log_for_error_message(
235+
f"Ignoring empty snapshot file {snapshot_name}"
236+
):
237+
raise AssertionError(
238+
f"Expected empty snapshot file {snapshot_name} to be skipped in node logs"
239+
)
240+
241+
208242
def split_all_ledger_files_in_dir(input_dir, output_dir):
209243
# A ledger file can only be split at a seqno that contains a signature
210244
# (so that all files end on a signature that verifies their integrity).
@@ -292,6 +326,7 @@ def run_file_operations(args):
292326
test_forced_ledger_chunk(network, args)
293327
test_forced_snapshot(network, args)
294328
test_large_snapshot(network, args)
329+
test_empty_snapshot(network, args)
295330

296331
primary, _ = network.find_primary()
297332
# Scoped transactions are not handled by historical range queries
@@ -589,6 +624,47 @@ def run_service_subject_name_check(args):
589624
assert cert.subject.rfc4514_string() == "CN=This test service", cert
590625

591626

627+
def run_empty_ledger_dir_check(args):
628+
with infra.network.network(
629+
args.nodes,
630+
args.binary_dir,
631+
args.debug_nodes,
632+
args.perf_nodes,
633+
pdb=args.pdb,
634+
) as network:
635+
LOG.info("Check that empty ledger directory is handled correctly")
636+
with tempfile.TemporaryDirectory() as tmp_dir:
637+
LOG.debug(f"Using {tmp_dir} as ledger directory")
638+
639+
dir_name = os.path.basename(tmp_dir)
640+
641+
# Check tmp_dir is indeed empty
642+
assert len(os.listdir(tmp_dir)) == 0, tmp_dir
643+
644+
# Start network, this should not fail
645+
network.start_and_open(args, ledger_dir=tmp_dir)
646+
primary, _ = network.find_primary()
647+
network.stop_all_nodes()
648+
649+
# Now write a file in the directory
650+
with open(os.path.join(tmp_dir, "ledger_1000_1500.committed"), "wb") as f:
651+
f.write(b"bar")
652+
653+
# Start new network, this should fail
654+
try:
655+
network.start(args, ledger_dir=tmp_dir)
656+
except Exception:
657+
pass
658+
659+
# Check that the node has failed with the expected error message
660+
if not primary.check_log_for_error_message(
661+
f"On start, ledger directory should not exist or be empty ({dir_name})"
662+
):
663+
raise AssertionError(
664+
f"Expected node error message with non-empty ledger directory {dir_name}"
665+
)
666+
667+
592668
def run(args):
593669
run_max_uncommitted_tx_count(args)
594670
run_file_operations(args)
@@ -599,3 +675,4 @@ def run(args):
599675
run_preopen_readiness_check(args)
600676
run_sighup_check(args)
601677
run_service_subject_name_check(args)
678+
run_empty_ledger_dir_check(args)

tests/infra/network.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ def _start_all_nodes(
465465
workspace=args.workspace,
466466
label=args.label,
467467
common_dir=self.common_dir,
468+
ledger_dir=ledger_dir,
468469
members_info=self.consortium.get_members_info(),
469470
**forwarded_args_with_overrides,
470471
**kwargs,

0 commit comments

Comments
 (0)