From a3be3c30b660afb0efbcf0cbf3d14134bb5924fc Mon Sep 17 00:00:00 2001 From: Victor Allombert Date: Wed, 17 Jul 2024 10:45:46 +0200 Subject: [PATCH 1/3] Shell: introduce Storage_maintenace.context_pruning flag --- src/lib_node_config/config_file.ml | 5 ++- src/lib_node_config/config_file.mli | 1 + src/lib_node_config/shared_arg.ml | 27 ++++++++++++- src/lib_node_config/shared_arg.mli | 1 + src/lib_shell_services/shell_limits.ml | 17 ++++++-- src/lib_shell_services/shell_limits.mli | 4 ++ src/lib_shell_services/storage_maintenance.ml | 40 +++++++++++++++++++ .../storage_maintenance.mli | 21 ++++++++++ 8 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 src/lib_shell_services/storage_maintenance.ml create mode 100644 src/lib_shell_services/storage_maintenance.mli diff --git a/src/lib_node_config/config_file.ml b/src/lib_node_config/config_file.ml index 2d9af34619c8..63f14a146b35 100644 --- a/src/lib_node_config/config_file.ml +++ b/src/lib_node_config/config_file.ml @@ -889,7 +889,8 @@ let update ?(disable_config_validation = false) ?data_dir ?min_connections ?(disable_mempool = default_p2p.disable_mempool) ?(enable_testchain = default_p2p.enable_testchain) ?(cors_origins = []) ?(cors_headers = []) ?rpc_tls ?log_output ?log_coloring - ?synchronisation_threshold ?history_mode ?network ?latency cfg = + ?synchronisation_threshold ?history_mode ?network ?latency ?context_pruning + cfg = let open Lwt_result_syntax in let disable_config_validation = cfg.disable_config_validation || disable_config_validation @@ -1011,6 +1012,8 @@ let update ?(disable_config_validation = false) ?data_dir ?min_connections in {synchronisation}); history_mode = Option.either history_mode cfg.shell.history_mode; + context_pruning = + Option.either context_pruning cfg.shell.context_pruning; } in (* If --network is specified it overrides the "network" entry of the diff --git a/src/lib_node_config/config_file.mli b/src/lib_node_config/config_file.mli index f440c67505c4..b6e9fe96d768 100644 --- a/src/lib_node_config/config_file.mli +++ b/src/lib_node_config/config_file.mli @@ -140,6 +140,7 @@ val update : ?history_mode:History_mode.t -> ?network:blockchain_network -> ?latency:int -> + ?context_pruning:Storage_maintenance.context_pruning -> t -> t tzresult Lwt.t diff --git a/src/lib_node_config/shared_arg.ml b/src/lib_node_config/shared_arg.ml index 1efde39fa01e..13fbf43b3705 100644 --- a/src/lib_node_config/shared_arg.ml +++ b/src/lib_node_config/shared_arg.ml @@ -70,6 +70,7 @@ type t = { metrics_addr : string list; operation_metadata_size_limit : Shell_limits.operation_metadata_size_limit option; + context_pruning : Storage_maintenance.context_pruning option; } type error += @@ -190,7 +191,8 @@ let wrap data_dir config_file network connections max_download_speed external_rpc_listen_addrs rpc_tls cors_origins cors_headers log_output log_coloring history_mode synchronisation_threshold latency disable_config_validation allow_all_rpc media_type - max_active_rpc_connections metrics_addr operation_metadata_size_limit = + max_active_rpc_connections metrics_addr operation_metadata_size_limit + context_pruning = let actual_data_dir = Option.value ~default:Config_file.default_data_dir data_dir in @@ -239,6 +241,7 @@ let wrap data_dir config_file network connections max_download_speed max_active_rpc_connections; metrics_addr; operation_metadata_size_limit; + context_pruning; } let process_command run = @@ -747,6 +750,25 @@ module Term = struct Config_file.default_max_active_rpc_connections & info ~docs ~doc ~docv:"NUM" ["max-active-rpc-connections"]) + let context_pruning = + let open Storage_maintenance in + let doc = + "Configures whether or not the storage maintenance of the context should \ + be enabled" + in + let parse str = + match str with + | "disabled" -> `Ok Disabled + | "enabled" -> `Ok Enabled + | _ -> + `Error + "context-pruning only supports \"disabled\" and \"enabled\" modes" + in + Arg.( + value + & opt (some (parse, pp_context_pruning)) None + & info ~docs ~doc ["context-pruning"]) + (* Args. *) let args = @@ -761,6 +783,7 @@ module Term = struct $ log_output $ log_coloring $ history_mode $ synchronisation_threshold $ latency $ disable_config_validation $ allow_all_rpc $ media_type $ max_active_rpc_connections $ metrics_addr $ operation_metadata_size_limit + $ context_pruning end let read_config_file args = @@ -908,6 +931,7 @@ let patch_config ?(may_override_network = false) ?(emit = Event.emit) max_active_rpc_connections; metrics_addr; operation_metadata_size_limit; + context_pruning; } = args in @@ -1065,6 +1089,7 @@ let patch_config ?(may_override_network = false) ?(emit = Event.emit) ?synchronisation_threshold ?history_mode ?latency + ?context_pruning cfg let read_and_patch_config_file ?may_override_network ?emit diff --git a/src/lib_node_config/shared_arg.mli b/src/lib_node_config/shared_arg.mli index 0212f58b336e..3468dd6eb10c 100644 --- a/src/lib_node_config/shared_arg.mli +++ b/src/lib_node_config/shared_arg.mli @@ -86,6 +86,7 @@ type t = { operation_metadata_size_limit : Shell_limits.operation_metadata_size_limit option; (** maximum operation metadata size allowed to be stored on disk *) + context_pruning : Storage_maintenance.context_pruning option; } val process_command : unit tzresult Lwt.t -> unit Cmdliner.Term.ret diff --git a/src/lib_shell_services/shell_limits.ml b/src/lib_shell_services/shell_limits.ml index 77bec879c68e..e8092ba29103 100644 --- a/src/lib_shell_services/shell_limits.ml +++ b/src/lib_shell_services/shell_limits.ml @@ -245,12 +245,15 @@ let chain_validator_limits_encoding = }); ]) +let default_storage_maintenance_context_pruning = Storage_maintenance.Enabled + type limits = { block_validator_limits : block_validator_limits; prevalidator_limits : prevalidator_limits; peer_validator_limits : peer_validator_limits; chain_validator_limits : chain_validator_limits; history_mode : History_mode.t option; + context_pruning : Storage_maintenance.context_pruning option; } let default_limits = @@ -260,6 +263,7 @@ let default_limits = peer_validator_limits = default_peer_validator_limits; chain_validator_limits = default_chain_validator_limits; history_mode = None; + context_pruning = Some Enabled; } let limits_encoding = @@ -271,25 +275,29 @@ let limits_encoding = prevalidator_limits; chain_validator_limits; history_mode; + context_pruning; } -> ( peer_validator_limits, block_validator_limits, prevalidator_limits, chain_validator_limits, - history_mode )) + history_mode, + context_pruning )) (fun ( peer_validator_limits, block_validator_limits, prevalidator_limits, chain_validator_limits, - history_mode ) -> + history_mode, + context_pruning ) -> { peer_validator_limits; block_validator_limits; prevalidator_limits; chain_validator_limits; history_mode; + context_pruning; }) - (obj5 + (obj6 (dft "peer_validator" peer_validator_limits_encoding @@ -306,4 +314,5 @@ let limits_encoding = "chain_validator" chain_validator_limits_encoding default_chain_validator_limits) - (opt "history_mode" History_mode.encoding)) + (opt "history_mode" History_mode.encoding) + (opt "context_pruning" Storage_maintenance.context_pruning_encoding)) diff --git a/src/lib_shell_services/shell_limits.mli b/src/lib_shell_services/shell_limits.mli index 522223b982ca..79a8ee85501f 100644 --- a/src/lib_shell_services/shell_limits.mli +++ b/src/lib_shell_services/shell_limits.mli @@ -93,12 +93,16 @@ val default_chain_validator_limits : chain_validator_limits val chain_validator_limits_encoding : chain_validator_limits Data_encoding.t +val default_storage_maintenance_context_pruning : + Storage_maintenance.context_pruning + type limits = { block_validator_limits : block_validator_limits; prevalidator_limits : prevalidator_limits; peer_validator_limits : peer_validator_limits; chain_validator_limits : chain_validator_limits; history_mode : History_mode.t option; + context_pruning : Storage_maintenance.context_pruning option; } val default_limits : limits diff --git a/src/lib_shell_services/storage_maintenance.ml b/src/lib_shell_services/storage_maintenance.ml new file mode 100644 index 000000000000..ccd56d0dac3a --- /dev/null +++ b/src/lib_shell_services/storage_maintenance.ml @@ -0,0 +1,40 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +type context_pruning = Enabled | Disabled + +let context_pruning_encoding = + let open Data_encoding in + def + "context_pruning" + ~title:"context_pruning" + ~description:"Context pruning status" + (union + ~tag_size:`Uint8 + [ + case + ~title:"disabled" + ~description: + "When disabled, the storage maintenance won't be triggered" + (Tag 0) + (constant "disabled") + (function Disabled -> Some () | _ -> None) + (fun () -> Disabled); + case + ~title:"enabled" + ~description: + "When enabled, the storage maintenance is triggered as soon as a \ + cycle dawn is encountered. This is the default value." + (Tag 1) + (constant "enabled") + (function Enabled -> Some () | _ -> None) + (fun () -> Enabled); + ]) + +let pp_context_pruning fmt = function + | Disabled -> Format.fprintf fmt "disabled" + | Enabled -> Format.fprintf fmt "enabled" diff --git a/src/lib_shell_services/storage_maintenance.mli b/src/lib_shell_services/storage_maintenance.mli new file mode 100644 index 000000000000..aed282bf3d64 --- /dev/null +++ b/src/lib_shell_services/storage_maintenance.mli @@ -0,0 +1,21 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +(** Storage maintenance configuration. + + Storage maintenance aims to configure the internals of the storage + maintenance procedure that aims to be run on regular basis. + + *) + +(** The type [context_pruning] specifies whether or not a storage maintenance + should be triggered (if [Enabled]) or not (if [Disabled]). *) +type context_pruning = Enabled | Disabled + +val context_pruning_encoding : context_pruning Data_encoding.t + +val pp_context_pruning : Format.formatter -> context_pruning -> unit -- GitLab From ca44e636861dbba75b317c979c2713e029325de8 Mon Sep 17 00:00:00 2001 From: Victor Allombert Date: Wed, 17 Jul 2024 15:57:00 +0200 Subject: [PATCH 2/3] Node: introduce context_pruning disable feature --- src/bin_node/node_run_command.ml | 1 + src/lib_shell/node.ml | 5 +- src/lib_shell/node.mli | 1 + src/lib_store/mocked/store.ml | 3 +- src/lib_store/shared/store_events.ml | 8 ++- src/lib_store/store.mli | 5 ++ src/lib_store/unix/block_store.ml | 30 +++++---- src/lib_store/unix/block_store.mli | 5 +- src/lib_store/unix/store.ml | 75 ++++++++++++++------- src/lib_store/unix/store.mli | 5 ++ src/lib_store/unix/test/test_block_store.ml | 5 ++ 11 files changed, 100 insertions(+), 43 deletions(-) diff --git a/src/bin_node/node_run_command.ml b/src/bin_node/node_run_command.ml index 080750afefcc..01b72bd0f295 100644 --- a/src/bin_node/node_run_command.ml +++ b/src/bin_node/node_run_command.ml @@ -349,6 +349,7 @@ let init_node ?sandbox ?target ~identity ~singleprocess ~internal_events Node.create ~sandboxed:(sandbox <> None) ?sandbox_parameters:(Option.map snd sandbox_param) + ?context_pruning:config.shell.context_pruning ~singleprocess ~version ~commit_info diff --git a/src/lib_shell/node.ml b/src/lib_shell/node.ml index f0d9248e4089..b44840e61c5e 100644 --- a/src/lib_shell/node.ml +++ b/src/lib_shell/node.ml @@ -216,7 +216,8 @@ let check_context_consistency store = let*! () = Node_event.(emit storage_corrupted_context_detected ()) in tzfail Non_recoverable_context -let create ?(sandboxed = false) ?sandbox_parameters ~singleprocess ~version +let create ?(sandboxed = false) ?sandbox_parameters + ?(context_pruning = Storage_maintenance.Enabled) ~singleprocess ~version ~commit_info { genesis; @@ -269,6 +270,7 @@ let create ?(sandboxed = false) ?sandbox_parameters ~singleprocess ~version ~context_dir:context_root ~allow_testchains:start_testchain ~readonly:false + ~context_pruning genesis in let main_chain_store = Store.main_chain_store store in @@ -310,6 +312,7 @@ let create ?(sandboxed = false) ?sandbox_parameters ~singleprocess ~version ~context_dir:context_root ~allow_testchains:start_testchain ~readonly:false + ~context_pruning genesis in return (validator_process, store) diff --git a/src/lib_shell/node.mli b/src/lib_shell/node.mli index 4a2f46366478..07310be53d69 100644 --- a/src/lib_shell/node.mli +++ b/src/lib_shell/node.mli @@ -56,6 +56,7 @@ type config = { val create : ?sandboxed:bool -> ?sandbox_parameters:Data_encoding.json -> + ?context_pruning:Storage_maintenance.context_pruning -> singleprocess:bool -> version:string -> commit_info:Octez_node_version.commit_info -> diff --git a/src/lib_store/mocked/store.ml b/src/lib_store/mocked/store.ml index ff8bd6981c1e..46a5a040392a 100644 --- a/src/lib_store/mocked/store.ml +++ b/src/lib_store/mocked/store.ml @@ -1824,7 +1824,8 @@ let store_dirs = ref [] let context_dirs = ref [] let init ?patch_context ?commit_genesis ?history_mode ?(readonly = false) - ?block_cache_limit ~store_dir ~context_dir ~allow_testchains genesis = + ?block_cache_limit ?context_pruning:_ ~store_dir ~context_dir + ~allow_testchains genesis = let open Lwt_result_syntax in if List.mem ~equal:String.equal context_dir !context_dirs then Format.kasprintf diff --git a/src/lib_store/shared/store_events.ml b/src/lib_store/shared/store_events.ml index 13e9995703f8..d0dbd8343ec1 100644 --- a/src/lib_store/shared/store_events.ml +++ b/src/lib_store/shared/store_events.ml @@ -30,12 +30,16 @@ let section = ["node"; "store"] (* Info *) let init_store = - declare_1 + declare_2 ~section ~level:Info ~name:"init_store" - ~msg:"initializing the store (readonly:{ro})" + ~msg: + "initializing the store (readonly:{ro}, \ + context_pruning:{context_pruning})" ("ro", Data_encoding.bool) + ~pp2:Storage_maintenance.pp_context_pruning + ("context_pruning", Storage_maintenance.context_pruning_encoding) let end_init_store = declare_0 diff --git a/src/lib_store/store.mli b/src/lib_store/store.mli index e7401bac868f..04557fcedb8f 100644 --- a/src/lib_store/store.mli +++ b/src/lib_store/store.mli @@ -206,6 +206,10 @@ type chain_store @param block_cache_limit allows to override the size of the block cache to use. The minimal value is 1. + @param context_pruning specifies whether or not the context + pruning is expected to be run (if set to Enabled) or not (if set + to Disabled) during a storage maintenance. + @param readonly a flag that, if set to true, prevent writing throughout the store {b and} context. Default: false @@ -218,6 +222,7 @@ val init : ?history_mode:History_mode.t -> ?readonly:bool -> ?block_cache_limit:int -> + ?context_pruning:Storage_maintenance.context_pruning -> store_dir:string -> context_dir:string -> allow_testchains:bool -> diff --git a/src/lib_store/unix/block_store.ml b/src/lib_store/unix/block_store.ml index 10599de44ac1..33e5e110bcce 100644 --- a/src/lib_store/unix/block_store.ml +++ b/src/lib_store/unix/block_store.ml @@ -1345,19 +1345,22 @@ let create_merging_thread block_store ~history_mode ~old_ro_store ~old_rw_store in return (new_ro_store, new_savepoint, new_caboose) -let may_trigger_gc block_store history_mode ~previous_savepoint ~new_savepoint = +let may_trigger_gc ~context_pruning block_store history_mode ~previous_savepoint + ~new_savepoint = let open Lwt_result_syntax in - let savepoint_hash = fst new_savepoint in - if - History_mode.(equal history_mode Archive) - || Block_hash.(savepoint_hash = fst previous_savepoint) - then (* No GC required *) return_unit - else - match block_store.gc_callback with - | None -> return_unit - | Some gc -> - let*! () = Store_events.(emit start_context_gc new_savepoint) in - gc savepoint_hash + if context_pruning = Storage_maintenance.Enabled then + let savepoint_hash = fst new_savepoint in + if + History_mode.(equal history_mode Archive) + || Block_hash.(savepoint_hash = fst previous_savepoint) + then (* No GC required *) return_unit + else + match block_store.gc_callback with + | None -> return_unit + | Some gc -> + let*! () = Store_events.(emit start_context_gc new_savepoint) in + gc savepoint_hash + else return_unit let split_context block_store new_head_lpbl = let open Lwt_result_syntax in @@ -1369,7 +1372,7 @@ let split_context block_store new_head_lpbl = let merge_stores ?(cycle_size_limit = default_cycle_size_limit) block_store ~(on_error : tztrace -> unit tzresult Lwt.t) ~finalizer ~history_mode - ~new_head ~new_head_metadata ~cementing_highwatermark = + ~new_head ~new_head_metadata ~cementing_highwatermark ~context_pruning = let open Lwt_result_syntax in let* () = fail_when block_store.readonly Cannot_write_in_readonly in (* Do not allow multiple merges: force waiting for a potential @@ -1460,6 +1463,7 @@ let merge_stores ?(cycle_size_limit = default_cycle_size_limit) block_store its end. *) let* () = may_trigger_gc + ~context_pruning block_store history_mode ~previous_savepoint diff --git a/src/lib_store/unix/block_store.mli b/src/lib_store/unix/block_store.mli index af6d17d6d685..2489f0456f9d 100644 --- a/src/lib_store/unix/block_store.mli +++ b/src/lib_store/unix/block_store.mli @@ -285,7 +285,9 @@ val default_cycle_size_limit : int32 After the cementing, {!Cemented_block_store.trigger_gc} will be called with the given [history_mode]. When the merging thread - succeeds, the callback [finalizer] will be called. + succeeds, the callback [finalizer] will be called. Note that + depending on [context_pruning], the context pruning may be + discarded. If a merge thread is already occurring, this function will first wait for the previous merge to be done. @@ -306,6 +308,7 @@ val merge_stores : new_head:Block_repr.t -> new_head_metadata:Block_repr.metadata -> cementing_highwatermark:int32 -> + context_pruning:Storage_maintenance.context_pruning -> unit tzresult Lwt.t val get_merge_status : t -> merge_status diff --git a/src/lib_store/unix/store.ml b/src/lib_store/unix/store.ml index d2bdb39705c4..559c944e1dc4 100644 --- a/src/lib_store/unix/store.ml +++ b/src/lib_store/unix/store.ml @@ -91,6 +91,7 @@ and chain_store = { (chain_store * block) Tezos_rpc.Directory.t Protocol_hash.Map.t Protocol_hash.Table.t; lockfile : Lwt_unix.file_descr; + context_pruning : Storage_maintenance.context_pruning; } and chain_state = { @@ -1577,24 +1578,30 @@ module Chain = struct created chunk will be ended by a commit that will be the target of a future gc call as reorganization may occur above the last preserved block level. Most of the time, and as reorganization - are often short, this will lead to the optimal behaviour. *) - let may_split_context chain_store new_head_lpbl previous_head = + are often short, this will lead to the optimal behaviour. + + As the split is necessary in the scope of the context pruning + only, it may be discarded depending on [context_pruning]. *) + let may_split_context ~context_pruning chain_store new_head_lpbl previous_head + = let open Lwt_result_syntax in - match history_mode chain_store with - | Archive -> return_unit - | Full _ | Rolling _ -> - let* previous_head_metadata = - Block.get_block_metadata chain_store previous_head - in - if - not - (Int32.equal - new_head_lpbl - (Block.last_preserved_block_level previous_head_metadata)) - then - let block_store = chain_store.block_store in - Block_store.split_context block_store new_head_lpbl - else return_unit + if context_pruning = Storage_maintenance.Enabled then + match history_mode chain_store with + | Archive -> return_unit + | Full _ | Rolling _ -> + let* previous_head_metadata = + Block.get_block_metadata chain_store previous_head + in + if + not + (Int32.equal + new_head_lpbl + (Block.last_preserved_block_level previous_head_metadata)) + then + let block_store = chain_store.block_store in + Block_store.split_context block_store new_head_lpbl + else return_unit + else return_unit let set_head chain_store new_head = let open Lwt_result_syntax in @@ -1646,7 +1653,13 @@ module Chain = struct let new_head_lpbl = Block.last_preserved_block_level new_head_metadata in - let* () = may_split_context chain_store new_head_lpbl previous_head in + let* () = + may_split_context + ~context_pruning:chain_store.context_pruning + chain_store + new_head_lpbl + previous_head + in let*! cementing_highwatermark = locked_determine_cementing_highwatermark chain_store @@ -1742,6 +1755,7 @@ module Chain = struct (WithExceptions.Option.get ~loc:__LOC__ cementing_highwatermark) + ~context_pruning:chain_store.context_pruning in (* The new memory highwatermark is new_head_lpbl, the disk value will be updated after the merge completion. *) @@ -2090,8 +2104,8 @@ module Chain = struct } let create_chain_store ?block_cache_limit global_store chain_dir ?target - ~chain_id ?(expiration = None) ?genesis_block ~genesis ~genesis_context - history_mode = + ~chain_id ?(expiration = None) ~context_pruning ?genesis_block ~genesis + ~genesis_context history_mode = let open Lwt_result_syntax in (* Chain directory *) let genesis_block = @@ -2137,12 +2151,13 @@ module Chain = struct validated_block_watcher; block_rpc_directories; lockfile; + context_pruning; } in return chain_store let load_chain_store ?block_cache_limit global_store chain_dir ~chain_id - ~readonly = + ~readonly ~context_pruning = let open Lwt_result_syntax in let* chain_config_data = Stored_data.load (Naming.chain_config_file chain_dir) @@ -2175,6 +2190,7 @@ module Chain = struct validated_block_watcher; block_rpc_directories; lockfile; + context_pruning; } in (* Also initalize the live blocks *) @@ -2248,6 +2264,7 @@ module Chain = struct testchain_dir ~chain_id ~readonly:false + ~context_pruning:Enabled in let testchain = {forked_block; testchain_store} in return_some testchain) @@ -2314,6 +2331,7 @@ module Chain = struct testchain_dir ~chain_id:testchain_id ~expiration:(Some expiration) + ~context_pruning:Enabled ~genesis_block ~genesis ~genesis_context @@ -2598,7 +2616,7 @@ end let create_store ?block_cache_limit ~context_index ~chain_id ~genesis ~genesis_context ?(history_mode = History_mode.default) ~allow_testchains - store_dir = + ~context_pruning store_dir = let open Lwt_result_syntax in let store_dir_path = Naming.dir_path store_dir in let*! () = Lwt_utils_unix.create_dir store_dir_path in @@ -2624,6 +2642,7 @@ let create_store ?block_cache_limit ~context_index ~chain_id ~genesis chain_dir ~chain_id ~expiration:None + ~context_pruning ~genesis ~genesis_context history_mode @@ -2632,7 +2651,7 @@ let create_store ?block_cache_limit ~context_index ~chain_id ~genesis return global_store let load_store ?history_mode ?block_cache_limit store_dir ~context_index - ~genesis ~chain_id ~allow_testchains ~readonly () = + ~genesis ~chain_id ~allow_testchains ~readonly ~context_pruning () = let open Lwt_result_syntax in let chain_dir = Naming.chain_dir store_dir chain_id in let* () = @@ -2685,6 +2704,7 @@ let load_store ?history_mode ?block_cache_limit store_dir ~context_index chain_dir ~chain_id ~readonly + ~context_pruning in let stored_genesis = Chain.genesis main_chain_store in let* () = @@ -2723,9 +2743,10 @@ let check_history_mode_consistency chain_dir history_mode = else (* Store is not yet initialized. *) return_unit let init ?patch_context ?commit_genesis ?history_mode ?(readonly = false) - ?block_cache_limit ~store_dir ~context_dir ~allow_testchains genesis = + ?block_cache_limit ?(context_pruning = Storage_maintenance.Enabled) + ~store_dir ~context_dir ~allow_testchains genesis = let open Lwt_result_syntax in - let*! () = Store_events.(emit init_store) readonly in + let*! () = Store_events.(emit init_store) (readonly, context_pruning) in let patch_context = Option.map (fun f ctxt -> @@ -2774,6 +2795,7 @@ let init ?patch_context ?commit_genesis ?history_mode ?(readonly = false) ~chain_id ~allow_testchains ~readonly + ~context_pruning () else (* Fresh store *) @@ -2783,6 +2805,7 @@ let init ?patch_context ?commit_genesis ?history_mode ?(readonly = false) store_dir ~context_index:(Context_ops.Disk_index context_index) ~chain_id + ~context_pruning ~genesis ~genesis_context ?history_mode @@ -2838,6 +2861,7 @@ let may_switch_history_mode ~store_dir ~context_dir genesis ~new_history_mode = ~chain_id ~allow_testchains:true ~readonly:false + ~context_pruning:Enabled () in let chain_store = main_chain_store store in @@ -3238,6 +3262,7 @@ module Unsafe = struct ~chain_id ~allow_testchains:false ~readonly:true + ~context_pruning:Disabled () in let chain_store = main_chain_store store in diff --git a/src/lib_store/unix/store.mli b/src/lib_store/unix/store.mli index 3e6a335eeacd..10e9d04c7a04 100644 --- a/src/lib_store/unix/store.mli +++ b/src/lib_store/unix/store.mli @@ -205,6 +205,10 @@ type chain_store @param block_cache_limit allows to override the size of the block cache to use. The minimal value is 1. + @param context_pruning specifies whether or not the context + pruning is expected to be run (if set to Enabled) or not (if set + to Disabled) during a storage maintenance. + @param readonly a flag that, if set to true, prevent writing throughout the store {b and} context. Default: false @@ -217,6 +221,7 @@ val init : ?history_mode:History_mode.t -> ?readonly:bool -> ?block_cache_limit:int -> + ?context_pruning:Storage_maintenance.context_pruning -> store_dir:string -> context_dir:string -> allow_testchains:bool -> diff --git a/src/lib_store/unix/test/test_block_store.ml b/src/lib_store/unix/test/test_block_store.ml index cb708e2b4c2d..3d22203ba253 100644 --- a/src/lib_store/unix/test/test_block_store.ml +++ b/src/lib_store/unix/test/test_block_store.ml @@ -259,6 +259,7 @@ let test_simple_merge block_store = ~new_head:head ~new_head_metadata:head_metadata ~cementing_highwatermark:0l + ~context_pruning:Enabled in let*! () = Block_store.await_merging block_store in assert_cemented_bound @@ -314,6 +315,7 @@ let test_consecutive_concurrent_merges block_store = ~new_head ~new_head_metadata ~cementing_highwatermark:previous_cycle_lpbl + ~context_pruning:Enabled in let threads = List.map merge_cycle cycles_to_merge in let*! res = Lwt.all threads in @@ -358,6 +360,7 @@ let test_ten_cycles_merge block_store = (head |> Block_repr.metadata |> WithExceptions.Option.to_exn ~none:Not_found) ~cementing_highwatermark:0l + ~context_pruning:Enabled in let* () = assert_presence_in_block_store ~with_metadata:true block_store all_blocks @@ -459,6 +462,7 @@ let test_merge_with_branches block_store = (head |> Block_repr.metadata |> WithExceptions.Option.to_exn ~none:Not_found) ~cementing_highwatermark:0l + ~context_pruning:Enabled in let*! () = Block_store.await_merging block_store in let* () = @@ -497,6 +501,7 @@ let perform_n_cycles_merge ?(cycle_length = 10) (head |> Block_repr.metadata |> WithExceptions.Option.to_exn ~none:Not_found) ~cementing_highwatermark:0l + ~context_pruning:Enabled in let*! () = Block_store.await_merging block_store in return cycles -- GitLab From 59f9aba9ad4668e19b000623381329efaca0ff8e Mon Sep 17 00:00:00 2001 From: Victor Allombert Date: Wed, 17 Jul 2024 16:55:54 +0200 Subject: [PATCH 3/3] Tezt: introduce context gc call test --- tezt/lib_tezos/node.ml | 8 +++- tezt/lib_tezos/node.mli | 1 + tezt/tests/main.ml | 1 + tezt/tests/storage_maintenance.ml | 71 +++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tezt/tests/storage_maintenance.ml diff --git a/tezt/lib_tezos/node.ml b/tezt/lib_tezos/node.ml index 365dc87f9f21..0f74708139cf 100644 --- a/tezt/lib_tezos/node.ml +++ b/tezt/lib_tezos/node.ml @@ -59,6 +59,7 @@ type argument = | RPC_additional_addr of string | RPC_additional_addr_external of string | Max_active_rpc_connections of int + | Context_pruning of string let make_argument = function | Network x -> ["--network"; x] @@ -92,6 +93,7 @@ let make_argument = function | RPC_additional_addr_external addr -> ["--external-rpc-addr"; addr] | Max_active_rpc_connections n -> ["--max-active-rpc-connections"; string_of_int n] + | Context_pruning x -> ["--context-pruning"; x] let make_arguments arguments = List.flatten (List.map make_argument arguments) @@ -114,7 +116,8 @@ let is_redundant = function | Media_type _, Media_type _ | Metadata_size_limit _, Metadata_size_limit _ | Version, Version - | Max_active_rpc_connections _, Max_active_rpc_connections _ -> + | Max_active_rpc_connections _, Max_active_rpc_connections _ + | Context_pruning _, Context_pruning _ -> true | Metrics_addr addr1, Metrics_addr addr2 -> addr1 = addr2 | Peer peer1, Peer peer2 -> peer1 = peer2 @@ -139,7 +142,8 @@ let is_redundant = function | RPC_additional_addr _, _ | RPC_additional_addr_external _, _ | Version, _ - | Max_active_rpc_connections _, _ -> + | Max_active_rpc_connections _, _ + | Context_pruning _, _ -> false (* Some arguments should not be written in the config file by [Node.init] diff --git a/tezt/lib_tezos/node.mli b/tezt/lib_tezos/node.mli index 55f83a20ca83..0876ae58905a 100644 --- a/tezt/lib_tezos/node.mli +++ b/tezt/lib_tezos/node.mli @@ -99,6 +99,7 @@ type argument = | RPC_additional_addr of string (** [--rpc-addr] *) | RPC_additional_addr_external of string (** [--external-rpc-addr] *) | Max_active_rpc_connections of int (** [--max-active-rpc-connections] *) + | Context_pruning of string (** [--context-pruning] *) (** A TLS configuration for the node: paths to a [.crt] and a [.key] file. diff --git a/tezt/tests/main.ml b/tezt/tests/main.ml index 53c743e70df8..ce3ff2134298 100644 --- a/tezt/tests/main.ml +++ b/tezt/tests/main.ml @@ -201,6 +201,7 @@ let register_protocol_tests_that_use_supports_correctly () = Sc_rollup.register ~protocols ; Self_address_transfer.register ~protocols ; Signer_test.register ~protocols ; + Storage_maintenance.register ~protocols ; Storage_reconstruction.register ~protocols ; Storage_snapshots.register ~protocols ; Stresstest_command.register ~protocols ; diff --git a/tezt/tests/storage_maintenance.ml b/tezt/tests/storage_maintenance.ml new file mode 100644 index 000000000000..a5b809b835c7 --- /dev/null +++ b/tezt/tests/storage_maintenance.ml @@ -0,0 +1,71 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs. *) +(* *) +(*****************************************************************************) +(* Testing + ------- + Component: Storage maintenance + Invocation: dune exec tezt/tests/main.exe -- -f storage_maintenance.ml + Subject: Tests the storage maintenance behaviour +*) + +let team = Tag.layer1 + +let bake_blocks node client ~blocks_to_bake = + Log.info "Baking a batch of %d blocks on %s" blocks_to_bake (Node.name node) ; + repeat blocks_to_bake @@ fun () -> + Client.bake_for_and_wait + ~endpoint:(Node node) + ~node + ~minimal_timestamp:true + client + +let wait_for_context_gc node ~expected = + Node.wait_for node "start_context_gc.v0" @@ fun _json -> + if expected then Some () + else Test.fail "Unexpected start_context_gc event caught" + +let wait_for_context_split node ~expected = + Node.wait_for node "start_context_split.v0" @@ fun _json -> + if expected then Some () + else Test.fail "Unexpected start_context_split event caught" + +let test_context_pruning_call = + Protocol.register_test + ~__FILE__ + ~title:(Format.asprintf "storage context pruning call") + ~tags:[team; "storage"; "maintenance"; "context"; "pruning"] + @@ fun protocol -> + let* node1, client = + Client.init_with_protocol + ~nodes_args:Node.[Synchronisation_threshold 0; Context_pruning "disabled"] + `Client + ~protocol + () + in + (* As the context pruning is enabled by default,we specify nothing + on the command line. *) + let* node2 = Node.init ~name:"with_gc" Node.[Synchronisation_threshold 0] in + let* () = Client.Admin.connect_address ~peer:node2 client in + let* (_ : int) = Node.wait_for_level node2 1 in + let blocks_per_cycle = 8 in + let blocks_to_bake = 2 * blocks_per_cycle in + let (_wait_no_context_gc : unit Lwt.t) = + wait_for_context_gc node1 ~expected:false + in + let (_wait_no_context_split : unit Lwt.t) = + wait_for_context_split node1 ~expected:false + in + let (wait_context_gc : unit Lwt.t) = + wait_for_context_gc node2 ~expected:true + in + let (wait_context_split : unit Lwt.t) = + wait_for_context_split node2 ~expected:true + in + let* () = bake_blocks node1 client ~blocks_to_bake in + let* () = wait_context_gc and* () = wait_context_split in + unit + +let register ~protocols = test_context_pruning_call protocols -- GitLab