diff --git a/src/lib_node_config/data_version.ml b/src/lib_node_config/data_version.ml index 4b44db1a237fd61ee5d6958604d03668b7c2b19e..5f05bc0c629532373d69d1438563e2c6c4ef987b 100644 --- a/src/lib_node_config/data_version.ml +++ b/src/lib_node_config/data_version.ml @@ -101,7 +101,8 @@ end * - 1.0 : context upgrade (upgrade to irmin.3.3) -- v14.0 * - 2.0 : introduce context GC (upgrade to irmin.3.4) -- v15.0 * - 3.0 : change blocks' context hash semantics and introduce - context split (upgrade to irmin.3.5) -- v16.0 *) + context split (upgrade to irmin.3.5) -- v16.0 + * - 3.1 : change encoding for block store status -- v21.0 *) (* FIXME https://gitlab.com/tezos/tezos/-/issues/2861 We should enable the semantic versioning instead of applying @@ -118,7 +119,9 @@ let v_2_0 = Version.make ~major:2 ~minor:0 let v_3_0 = Version.make ~major:3 ~minor:0 -let current_version = v_3_0 +let v_3_1 = Version.make ~major:3 ~minor:1 + +let current_version = v_3_1 (* List of upgrade functions from each still supported previous version to the current [data_version] above. If this list grows too @@ -141,29 +144,32 @@ let upgradable_data_version = let*! () = Context.close ctxt in return_unit in - let v_3_0_upgrade ~data_dir genesis = + let v_3_1_upgrade ~data_dir genesis ~upgrade_to_v3 = let store_dir = store_dir data_dir in - Store.v_3_0_upgrade ~store_dir genesis + Store.v_3_1_upgrade ~store_dir genesis ~upgrade_to_v3 in [ ( v_0_6, fun ~data_dir genesis ~chain_name:_ ~sandbox_parameters:_ -> let* () = v_1_0_upgrade ~data_dir in - v_3_0_upgrade ~data_dir genesis ); + v_3_1_upgrade ~data_dir genesis ~upgrade_to_v3:true ); ( v_0_7, fun ~data_dir genesis ~chain_name:_ ~sandbox_parameters:_ -> let* () = v_1_0_upgrade ~data_dir in - v_3_0_upgrade ~data_dir genesis ); + v_3_1_upgrade ~data_dir genesis ~upgrade_to_v3:true ); ( v_0_8, fun ~data_dir genesis ~chain_name:_ ~sandbox_parameters:_ -> let* () = v_1_0_upgrade ~data_dir in - v_3_0_upgrade ~data_dir genesis ); + v_3_1_upgrade ~data_dir genesis ~upgrade_to_v3:true ); ( v_1_0, fun ~data_dir genesis ~chain_name:_ ~sandbox_parameters:_ -> - v_3_0_upgrade ~data_dir genesis ); + v_3_1_upgrade ~data_dir genesis ~upgrade_to_v3:true ); ( v_2_0, fun ~data_dir genesis ~chain_name:_ ~sandbox_parameters:_ -> - v_3_0_upgrade ~data_dir genesis ); + v_3_1_upgrade ~data_dir genesis ~upgrade_to_v3:true ); + ( v_3_0, + fun ~data_dir genesis ~chain_name:_ ~sandbox_parameters:_ -> + v_3_1_upgrade ~data_dir genesis ~upgrade_to_v3:false ); ] type error += Invalid_data_dir_version of Version.t * Version.t @@ -456,8 +462,9 @@ let ensure_data_dir ?(mode = Is_compatible) genesis data_dir = (* Enable automatic upgrade to avoid users to manually upgrade. *) | Some (version, _) when Version.( - equal version v_2_0 || equal version v_1_0 || equal version v_0_6 - || equal version v_0_7 || equal version v_0_8) -> + equal version v_3_0 || equal version v_2_0 || equal version v_1_0 + || equal version v_0_6 || equal version v_0_7 || equal version v_0_8) + -> let* () = upgrade_data_dir ~data_dir genesis ~chain_name:() ~sandbox_parameters:() in diff --git a/src/lib_store/mocked/store.ml b/src/lib_store/mocked/store.ml index ffbe1c1dc9ddf72e69857407eda4db376007c47e..97c38702a887fb05c2a7aa77a21b65f14fd04dc7 100644 --- a/src/lib_store/mocked/store.ml +++ b/src/lib_store/mocked/store.ml @@ -2077,3 +2077,6 @@ module Unsafe = struct end let v_3_0_upgrade ~store_dir:_ _genesis = Lwt_result_syntax.return_unit + +let v_3_1_upgrade ~store_dir:_ _genesis ~upgrade_to_v3:_ = + Lwt_result_syntax.return_unit diff --git a/src/lib_store/shared/naming.ml b/src/lib_store/shared/naming.ml index 12bcb6202906742f603e9b7627abdba53f12d8b2..f8716b9d5532dacfca382289f96e38b3045625a5 100644 --- a/src/lib_store/shared/naming.ml +++ b/src/lib_store/shared/naming.ml @@ -151,27 +151,19 @@ let caboose_file dir = block_descriptor_encoding Store_types.block_descriptor_equal -type block_store_status = Idle | Merging - -let block_store_status_encoding = - let open Data_encoding in - conv - (function Idle -> false | Merging -> true) - (function false -> Idle | true -> Merging) - bool - -let status_equal s1 s2 = - match (s1, s2) with - | Idle, Idle -> true - | Merging, Merging -> true - | Idle, Merging | Merging, Idle -> false - let block_store_status_file dir = make_encoded_file dir ~filename:"status" - block_store_status_encoding - status_equal + Block_store_status.encoding + Block_store_status.equal + +let legacy_block_store_status_file dir = + make_encoded_file + dir + ~filename:"status" + Block_store_status.Legacy.encoding + Block_store_status.Legacy.equal let cemented_blocks_dir dir = mk_dir dir "cemented" diff --git a/src/lib_store/shared/naming.mli b/src/lib_store/shared/naming.mli index 850036e9292df2593ed0e80e164235179b4ce95a..7343d0a9962592a6ef873dcfce8f047452ed6e96 100644 --- a/src/lib_store/shared/naming.mli +++ b/src/lib_store/shared/naming.mli @@ -133,10 +133,12 @@ val savepoint_file : val caboose_file : [`Chain_dir] directory -> ([`Caboose], block_descriptor) encoded_file -type block_store_status = Idle | Merging - val block_store_status_file : - [`Chain_dir] directory -> ([`Status], block_store_status) encoded_file + [`Chain_dir] directory -> ([`Status], Block_store_status.t) encoded_file + +val legacy_block_store_status_file : + [`Chain_dir] directory -> + ([`Status], Block_store_status.Legacy.t) encoded_file val cemented_blocks_dir : [< `Chain_dir | `Snapshot_dir | `Snapshot_tmp_dir | `Tar_archive] directory -> diff --git a/src/lib_store/shared/store_events.ml b/src/lib_store/shared/store_events.ml index 54abbced2f7f355ebf55fce53f0d4b1cc18f29f8..049b4f3e3e8bffefb3f7b1906486afdce731802f 100644 --- a/src/lib_store/shared/store_events.ml +++ b/src/lib_store/shared/store_events.ml @@ -201,6 +201,22 @@ let start_retreiving_cycles = ~msg:"retrieving cycles from floating store" () +let load_block_store_status = + declare_0 + ~section + ~level:Info + ~name:"load_block_store_status" + ~msg:"loading using legacy block_store_status encoding" + () + +let fixed_block_store_status = + declare_0 + ~section + ~level:Info + ~name:"fixed_block_store_status" + ~msg:"loading of block store status was successful" + () + let store_is_consistent = declare_0 ~section diff --git a/src/lib_store/shared/store_types.ml b/src/lib_store/shared/store_types.ml index 80a744ee744c0dbceab3abe6f7974e375ca26e59..ecf81132e337b688c72765a834271427d845a539 100644 --- a/src/lib_store/shared/store_types.ml +++ b/src/lib_store/shared/store_types.ml @@ -23,6 +23,108 @@ (* *) (*****************************************************************************) +(* block_store_status *) + +module Block_store_status = struct + type t = Idle of int | Merging of int + + let get_value status_data = + let open Lwt_syntax in + let* status = Stored_data.get status_data in + match status with + | Idle value -> return value + | Merging value -> return value + + let set_idle_status status_data = + let open Lwt_syntax in + let* status_value = get_value status_data in + Stored_data.write status_data (Idle (status_value + 1)) + + let set_merge_status status_data = + let open Lwt_syntax in + let* status_value = get_value status_data in + Stored_data.write status_data (Merging (status_value + 1)) + + let is_idle = function Idle _ -> true | Merging _ -> false + + let is_merging = function Idle _ -> false | Merging _ -> true + + let create_idle_status = Idle 0 + + let equal s1 s2 = + match (s1, s2) with + | Idle c1, Idle c2 when c1 = c2 -> true + | Merging c1, Merging c2 when c1 = c2 -> true + | Idle _, Idle _ + | Merging _, Merging _ + | Idle _, Merging _ + | Merging _, Idle _ -> + false + + let encoding = + let open Data_encoding in + union + ~tag_size:`Uint8 + [ + case + (Tag 0) + ~title:"idle" + int31 + (function Idle c -> Some c | _ -> None) + (fun c -> Idle c); + case + (Tag 1) + ~title:"merging" + int31 + (function Merging c -> Some c | _ -> None) + (fun c -> Merging c); + ] + + let pp ppf = function + | Idle i -> Format.fprintf ppf "Idle %d" i + | Merging i -> Format.fprintf ppf "Merging %d" i + + module Legacy = struct + type t = Idle | Merging + + let set_idle_status status_data = Stored_data.write status_data Idle + + let set_merge_status status_data = Stored_data.write status_data Merging + + let is_idle = function Idle -> true | Merging -> false + + let is_merging = function Idle -> false | Merging -> true + + let create_idle_status = Idle + + let equal s1 s2 = + match (s1, s2) with + | Idle, Idle -> true + | Merging, Merging -> true + | Idle, Merging | Merging, Idle -> false + + let encoding = + let open Data_encoding in + conv + (function Idle -> false | Merging -> true) + (function false -> Idle | true -> Merging) + bool + + let pp ppf = function + | Idle -> Format.fprintf ppf "Idle" + | Merging -> Format.fprintf ppf "Merging" + end + + let of_legacy legacy_status_data = + let open Lwt_result_syntax in + let*! legacy_status = Stored_data.get legacy_status_data in + match legacy_status with + | Legacy.Idle -> return (Idle 0) + | Merging -> return (Merging 0) +end + +(* block_descriptor *) + type block_descriptor = Block_hash.t * int32 let block_descriptor_equal (bh1, lvl1) (bh2, lvl2) = @@ -35,6 +137,8 @@ let block_descriptor_encoding = let pp_block_descriptor fmt (hash, level) = Format.fprintf fmt "%a (level: %ld)" Block_hash.pp hash level +(* chain_config *) + type chain_config = { history_mode : History_mode.t; genesis : Genesis.t; @@ -58,6 +162,8 @@ let chain_config_encoding = (req "genesis" Genesis.encoding) (varopt "expiration" Time.Protocol.encoding)) +(* invalid_block *) + type invalid_block = {level : int32; errors : Error_monad.error list} let invalid_block_equal {level = l1; errors = e1} {level = l2; errors = e2} = diff --git a/src/lib_store/shared/store_types.mli b/src/lib_store/shared/store_types.mli index dc5b9aeabdb126886394ae9bf3ea7a9ddd0b21d9..e2cce0135071c6519a369c4dade3b7c7d732f8aa 100644 --- a/src/lib_store/shared/store_types.mli +++ b/src/lib_store/shared/store_types.mli @@ -25,6 +25,62 @@ (** {1 Global types used in the store library} *) +module Block_store_status : sig + (** The type used to describe the status of the store. *) + type t + + (* Getters *) + val get_value : t Stored_data.t -> int Lwt.t + + (* Setters *) + + val set_idle_status : t Stored_data.t -> unit tzresult Lwt.t + + val set_merge_status : t Stored_data.t -> unit tzresult Lwt.t + + (* Status querying *) + + val is_idle : t -> bool + + val is_merging : t -> bool + + (* Initialisers *) + + val create_idle_status : t + + (* Equality function *) + val equal : t -> t -> bool + + (* Encoding *) + val encoding : t Data_encoding.t + + (* Printing *) + + val pp : Format.formatter -> t -> unit + + module Legacy : sig + type t + + val set_idle_status : t Stored_data.t -> unit tzresult Lwt.t + + val set_merge_status : t Stored_data.t -> unit tzresult Lwt.t + + val is_idle : t -> bool + + val is_merging : t -> bool + + val create_idle_status : t + + val equal : t -> t -> bool + + val encoding : t Data_encoding.t + + val pp : Format.formatter -> t -> unit + end + + val of_legacy : Legacy.t Stored_data.t -> t tzresult Lwt.t +end + (** The type used to describe a block pointer i.e. its hash and level. *) type block_descriptor = Block_hash.t * int32 diff --git a/src/lib_store/store.mli b/src/lib_store/store.mli index 7221661a614aa6e794464aed74b4cc5f4c80daf2..894c4666fda6c75a1b355ebbc9a762be1df0c497 100644 --- a/src/lib_store/store.mli +++ b/src/lib_store/store.mli @@ -1021,6 +1021,9 @@ end val v_3_0_upgrade : store_dir:string -> Genesis.t -> unit tzresult Lwt.t +val v_3_1_upgrade : + store_dir:string -> Genesis.t -> upgrade_to_v3:bool -> unit tzresult Lwt.t + (**/**) (** Unsafe set of functions intended for internal store manipulation diff --git a/src/lib_store/unix/block_store.ml b/src/lib_store/unix/block_store.ml index e4c07b573bc00253dfce78f5d6ead1fd5f249d40..f68a9199af87a2305a283b7a867c701bd6b3d31a 100644 --- a/src/lib_store/unix/block_store.ml +++ b/src/lib_store/unix/block_store.ml @@ -31,8 +31,6 @@ let default_block_cache_limit = 1_000 type merge_status = Not_running | Running | Merge_failed of tztrace -type status = Naming.block_store_status = Idle | Merging - type block_store = { chain_dir : [`Chain_dir] Naming.directory; readonly : bool; @@ -42,7 +40,7 @@ type block_store = { mutable rw_floating_block_store : Floating_block_store.t; caboose : block_descriptor Stored_data.t; savepoint : block_descriptor Stored_data.t; - status_data : status Stored_data.t; + status_data : Block_store_status.t Stored_data.t; block_cache : Block_repr.t Block_lru_cache.t; mutable gc_callback : (Block_hash.t -> unit tzresult Lwt.t) option; mutable split_callback : (unit -> unit tzresult Lwt.t) option; @@ -56,15 +54,6 @@ type t = block_store type key = Block of (Block_hash.t * int) -let status_encoding = - let open Data_encoding in - conv - (function Idle -> false | Merging -> true) - (function false -> Idle | true -> Merging) - Data_encoding.bool - -let status_to_string = function Idle -> "idle" | Merging -> "merging" - let cemented_block_store {cemented_store; _} = cemented_store let floating_block_stores {ro_floating_block_stores; rw_floating_block_store; _} @@ -97,8 +86,6 @@ let write_caboose {caboose; _} v = let genesis_block {genesis_block; _} = genesis_block -let write_status {status_data; _} status = Stored_data.write status_data status - (** [global_predecessor_lookup chain_block_store hash pow_nth] retrieves the 2^[pow_nth] predecessor's hash from the block with corresponding [hash] by checking all stores iteratively. Returns [None] if the @@ -1396,11 +1383,12 @@ let merge_stores ?(cycle_size_limit = default_cycle_size_limit) block_store let*! store_status = status block_store in let* () = fail_unless - (store_status = Idle) - (Cannot_merge_store {status = status_to_string store_status}) + (Block_store_status.is_idle store_status) + (Cannot_merge_store + {status = Format.asprintf "%a" Block_store_status.pp store_status}) in (* Mark the store's status as Merging *) - let* () = write_status block_store Merging in + let* () = Block_store_status.set_merge_status block_store.status_data in let new_head_lpbl = Block_repr.last_preserved_block_level new_head_metadata in @@ -1483,7 +1471,10 @@ let merge_stores ?(cycle_size_limit = default_cycle_size_limit) block_store let* () = finalizer new_head_lpbl in (* The merge operation succeeded, the store is now idle. *) block_store.merging_thread <- None ; - let* () = write_status block_store Idle in + let* () = + Block_store_status.set_idle_status + block_store.status_data + in return_unit)) (fun () -> Lwt_mutex.unlock block_store.merge_mutex ; @@ -1559,7 +1550,7 @@ let merge_temporary_floating block_store = let*! rw = Floating_block_store.init chain_dir ~readonly:false RW in block_store.ro_floating_block_stores <- [ro] ; block_store.rw_floating_block_store <- rw ; - write_status block_store Idle + Block_store_status.set_idle_status block_store.status_data (* Removes the potentially leftover temporary files from the cementing of cycles. *) @@ -1591,12 +1582,11 @@ let may_recover_merge block_store = let* () = Lwt_idle_waiter.force_idle block_store.merge_scheduler (fun () -> Lwt_mutex.with_lock block_store.merge_mutex (fun () -> - let*! d = Stored_data.get block_store.status_data in - match d with - | Idle -> return_unit - | Merging -> - let*! () = Store_events.(emit recover_merge ()) in - merge_temporary_floating block_store)) + let*! status = Stored_data.get block_store.status_data in + if Block_store_status.is_idle status then return_unit + else + let*! () = Store_events.(emit recover_merge ()) in + merge_temporary_floating block_store)) in (* Try to clean temporary file anyway. *) let*! () = may_clean_cementing_artifacts block_store in @@ -1632,7 +1622,7 @@ let load ?block_cache_limit chain_dir ~genesis_block ~readonly = let* status_data = Stored_data.init (Naming.block_store_status_file chain_dir) - ~initial_data:Idle + ~initial_data:Block_store_status.create_idle_status in let block_cache = Block_lru_cache.create @@ -1663,7 +1653,9 @@ let load ?block_cache_limit chain_dir ~genesis_block ~readonly = if not readonly then may_recover_merge block_store else return_unit in let*! status = Stored_data.get status_data in - let* () = fail_unless (status = Idle) Cannot_load_degraded_store in + let* () = + fail_unless (Block_store_status.is_idle status) Cannot_load_degraded_store + in return block_store let create ?block_cache_limit chain_dir ~genesis_block = @@ -1840,3 +1832,21 @@ let v_3_0_upgrade chain_dir ~cleanups ~finalizers = Lwt.return_unit) in protect (fun () -> List.iter_es upgrade_floating_index all_kinds) + +let v_3_1_upgrade chain_dir = + let open Lwt_result_syntax in + (* Load using the legacy block_store_status encoding *) + let*! () = Store_events.(emit load_block_store_status ()) in + let* legacy_status_data = + Stored_data.init + (Naming.legacy_block_store_status_file chain_dir) + ~initial_data:Block_store_status.Legacy.create_idle_status + in + (* Convert to the new encoding *) + let* status = Block_store_status.of_legacy legacy_status_data in + (* Overwrite the status file *) + let* () = + Stored_data.write_file (Naming.block_store_status_file chain_dir) status + in + let*! () = Store_events.(emit fixed_block_store_status ()) in + return_unit diff --git a/src/lib_store/unix/block_store.mli b/src/lib_store/unix/block_store.mli index b12bb60381ce5cccba59f4ddc2a5cad75aae6207..f1b0172d57c8e86e9b523c4e1af760bd4316dcbf 100644 --- a/src/lib_store/unix/block_store.mli +++ b/src/lib_store/unix/block_store.mli @@ -164,11 +164,6 @@ type key = Block of (Block_hash.t * int) (** The status of the merging thread *) type merge_status = Not_running | Running | Merge_failed of tztrace -(** The status of the store *) -type status = Idle | Merging - -val status_encoding : status Data_encoding.t - (** [cemented_block_store block_store] returns the instance of the cemented block store for [block_store]. *) val cemented_block_store : block_store -> Cemented_block_store.t @@ -193,9 +188,7 @@ val caboose : block_store -> block_descriptor Lwt.t val write_caboose : block_store -> block_descriptor -> unit tzresult Lwt.t -val status : block_store -> status Lwt.t - -val write_status : block_store -> status -> unit tzresult Lwt.t +val status : block_store -> Block_store_status.t Lwt.t val genesis_block : block_store -> Block_repr.t @@ -391,3 +384,6 @@ val v_3_0_upgrade : cleanups:(unit -> unit Lwt.t) list ref -> finalizers:(unit -> unit Lwt.t) list ref -> unit tzresult Lwt.t + +(** Upgrade the block_store_status *) +val v_3_1_upgrade : [`Chain_dir] Naming.directory -> unit tzresult Lwt.t diff --git a/src/lib_store/unix/store.ml b/src/lib_store/unix/store.ml index e74bee8e41f84a81b14bc5c1c386b7cc3b3e966c..f3c3de7aa97d1d0df657c65f96ce48056b5fbcb6 100644 --- a/src/lib_store/unix/store.ml +++ b/src/lib_store/unix/store.ml @@ -1674,7 +1674,7 @@ module Chain = struct (* We mark the merge as on-going to prevent the merge from being triggered and to update on-disk values. *) return_true - | Not_running when store_status <> Idle -> + | Not_running when not @@ Block_store_status.is_idle store_status -> (* Degenerate case, do the same as the Merge_failed case *) let*! () = Store_events.(emit notify_merge_error []) in return_true @@ -3246,6 +3246,18 @@ let v_3_0_upgrade ~store_dir genesis = let*! () = List.iter_s (fun f -> f ()) !finalizers in return_unit) +let v_3_1_upgrade ~store_dir genesis ~upgrade_to_v3 = + let open Lwt_result_syntax in + let* () = + if upgrade_to_v3 then v_3_0_upgrade ~store_dir genesis else return_unit + in + (* Hypothesis: v_3_0_upgrade has been run before or is run here *) + let chain_id = Chain_id.of_block_hash genesis.Genesis.block in + let chain_dir = + Naming.chain_dir (Naming.store_dir ~dir_path:store_dir) chain_id + in + Block_store.v_3_1_upgrade chain_dir + (************ For testing and internal purposes only **************) module Unsafe = struct let repr_of_block b = b diff --git a/src/lib_store/unix/store.mli b/src/lib_store/unix/store.mli index 8be00d0f59ad7caf9deb157352366d40f545efc2..22bf36322b17cb2fd27a3567aedd94624ebb9615 100644 --- a/src/lib_store/unix/store.mli +++ b/src/lib_store/unix/store.mli @@ -1019,6 +1019,10 @@ end {b Warning} Not backward-compatible. *) val v_3_0_upgrade : store_dir:string -> Genesis.t -> unit tzresult Lwt.t +(** Potentially upgrade to v_3 and then upgrade the block_store_status in v_3_1. *) +val v_3_1_upgrade : + store_dir:string -> Genesis.t -> upgrade_to_v3:bool -> unit tzresult Lwt.t + (**/**) (** Unsafe set of functions intended for internal store manipulation