2023-11-09 14:31:59 +00:00
use std ::collections ::HashSet ;
2023-11-08 16:49:06 +00:00
use garage_util ::crdt ::{ Crdt , Lww , LwwMap } ;
use garage_util ::data ::* ;
use garage_util ::encode ::nonversioned_encode ;
use garage_util ::error ::* ;
use super ::schema ::* ;
use super ::* ;
2023-11-15 13:20:50 +00:00
impl LayoutHistory {
pub fn new ( replication_factor : usize ) -> Self {
let version = LayoutVersion ::new ( replication_factor ) ;
let staging = LayoutStaging {
parameters : Lww ::< LayoutParameters > ::new ( version . parameters ) ,
roles : LwwMap ::new ( ) ,
} ;
LayoutHistory {
versions : vec ! [ version ] ,
update_trackers : Default ::default ( ) ,
staging : Lww ::raw ( 0 , staging ) ,
}
2023-11-09 14:31:59 +00:00
}
2023-11-15 13:20:50 +00:00
// ------------------ who stores what now? ---------------
pub fn current ( & self ) -> & LayoutVersion {
self . versions . last ( ) . as_ref ( ) . unwrap ( )
2023-11-15 12:28:30 +00:00
}
2023-11-15 13:20:50 +00:00
pub fn min_stored ( & self ) -> u64 {
self . versions . first ( ) . as_ref ( ) . unwrap ( ) . version
2023-11-09 14:31:59 +00:00
}
2023-11-15 13:20:50 +00:00
pub fn get_all_nodes ( & self ) -> Vec < Uuid > {
if self . versions . len ( ) = = 1 {
self . versions [ 0 ] . all_nodes ( ) . to_vec ( )
} else {
let set = self
. versions
. iter ( )
. map ( | x | x . all_nodes ( ) )
. flatten ( )
. collect ::< HashSet < _ > > ( ) ;
set . into_iter ( ) . copied ( ) . collect ::< Vec < _ > > ( )
}
2023-11-09 14:31:59 +00:00
}
2023-11-16 12:26:43 +00:00
pub ( crate ) fn get_all_nongateway_nodes ( & self ) -> Vec < Uuid > {
2023-11-15 13:20:50 +00:00
if self . versions . len ( ) = = 1 {
self . versions [ 0 ] . nongateway_nodes ( ) . to_vec ( )
} else {
let set = self
. versions
. iter ( )
. map ( | x | x . nongateway_nodes ( ) )
. flatten ( )
. collect ::< HashSet < _ > > ( ) ;
set . into_iter ( ) . copied ( ) . collect ::< Vec < _ > > ( )
}
2023-11-09 14:31:59 +00:00
}
2023-11-15 13:20:50 +00:00
// ---- housekeeping (all invoked by LayoutHelper) ----
2023-11-16 12:26:43 +00:00
pub ( crate ) fn cleanup_old_versions ( & mut self ) {
// If there are invalid versions before valid versions, remove them
if self . versions . len ( ) > 1 & & self . current ( ) . check ( ) . is_ok ( ) {
while self . versions . len ( ) > 1 & & self . versions . first ( ) . unwrap ( ) . check ( ) . is_err ( ) {
let removed = self . versions . remove ( 0 ) ;
info! (
" Layout history: pruning old invalid version {} " ,
removed . version
) ;
}
}
// If there are old versions that no one is reading from anymore,
// remove them
while self . versions . len ( ) > 1 {
2023-11-15 13:20:50 +00:00
let all_nongateway_nodes = self . get_all_nongateway_nodes ( ) ;
let min_version = self . min_stored ( ) ;
let sync_ack_map_min = self
. update_trackers
. sync_ack_map
. min ( & all_nongateway_nodes , min_version ) ;
if self . min_stored ( ) < sync_ack_map_min {
let removed = self . versions . remove ( 0 ) ;
info! ( " Layout history: pruning old version {} " , removed . version ) ;
} else {
break ;
}
2023-11-09 14:31:59 +00:00
}
}
2023-11-16 12:26:43 +00:00
pub ( crate ) fn clamp_update_trackers ( & mut self , nodes : & [ Uuid ] ) {
2023-11-15 13:20:50 +00:00
let min_v = self . min_stored ( ) ;
for node in nodes {
self . update_trackers . ack_map . set_max ( * node , min_v ) ;
self . update_trackers . sync_map . set_max ( * node , min_v ) ;
self . update_trackers . sync_ack_map . set_max ( * node , min_v ) ;
}
}
2023-11-16 12:26:43 +00:00
pub ( crate ) fn calculate_trackers_hash ( & self ) -> Hash {
2023-11-15 13:20:50 +00:00
blake2sum ( & nonversioned_encode ( & self . update_trackers ) . unwrap ( ) [ .. ] )
}
2023-11-16 12:26:43 +00:00
pub ( crate ) fn calculate_staging_hash ( & self ) -> Hash {
2023-11-15 13:20:50 +00:00
blake2sum ( & nonversioned_encode ( & self . staging ) . unwrap ( ) [ .. ] )
}
2023-11-08 16:49:06 +00:00
// ================== updates to layout, public interface ===================
pub fn merge ( & mut self , other : & LayoutHistory ) -> bool {
let mut changed = false ;
// Add any new versions to history
for v2 in other . versions . iter ( ) {
2023-11-08 18:28:36 +00:00
if let Some ( v1 ) = self . versions . iter ( ) . find ( | v | v . version = = v2 . version ) {
2023-11-16 12:26:43 +00:00
// Version is already present, check consistency
2023-11-08 16:49:06 +00:00
if v1 ! = v2 {
error! ( " Inconsistent layout histories: different layout compositions for version {}. Your cluster will be broken as long as this layout version is not replaced. " , v2 . version ) ;
}
2023-11-08 18:28:36 +00:00
} else if self . versions . iter ( ) . all ( | v | v . version ! = v2 . version - 1 ) {
2023-11-08 16:49:06 +00:00
error! (
" Cannot receive new layout version {}, version {} is missing " ,
v2 . version ,
v2 . version - 1
) ;
} else {
2023-11-08 18:28:36 +00:00
self . versions . push ( v2 . clone ( ) ) ;
2023-11-08 16:49:06 +00:00
changed = true ;
}
}
// Merge trackers
2023-11-16 12:26:43 +00:00
let c = self . update_trackers . merge ( & other . update_trackers ) ;
changed = changed | | c ;
2023-11-15 12:28:30 +00:00
2023-11-09 13:53:34 +00:00
// Merge staged layout changes
if self . staging ! = other . staging {
2023-11-16 12:26:43 +00:00
let prev_staging = self . staging . clone ( ) ;
2023-11-09 13:53:34 +00:00
self . staging . merge ( & other . staging ) ;
2023-11-16 12:26:43 +00:00
changed = changed | | self . staging ! = prev_staging ;
2023-11-09 13:53:34 +00:00
}
2023-11-08 16:49:06 +00:00
changed
}
pub fn apply_staged_changes ( mut self , version : Option < u64 > ) -> Result < ( Self , Message ) , Error > {
match version {
None = > {
let error = r #"
Please pass the new layout version number to ensure that you are writing the correct version of the cluster layout .
To know the correct value of the new layout version , invoke ` garage layout show ` and review the proposed changes .
" #;
return Err ( Error ::Message ( error . into ( ) ) ) ;
}
Some ( v ) = > {
if v ! = self . current ( ) . version + 1 {
return Err ( Error ::Message ( " Invalid new layout version " . into ( ) ) ) ;
}
}
}
2023-11-09 10:19:43 +00:00
// Compute new version and add it to history
2023-11-14 11:48:38 +00:00
let ( new_version , msg ) = self
. current ( )
. clone ( )
. calculate_next_version ( & self . staging . get ( ) ) ? ;
2023-11-08 16:49:06 +00:00
2023-11-08 18:28:36 +00:00
self . versions . push ( new_version ) ;
2023-11-16 12:26:43 +00:00
self . cleanup_old_versions ( ) ;
2023-11-08 16:49:06 +00:00
2023-11-09 10:19:43 +00:00
// Reset the staged layout changes
self . staging . update ( LayoutStaging {
parameters : self . staging . get ( ) . parameters . clone ( ) ,
roles : LwwMap ::new ( ) ,
} ) ;
2023-11-08 16:49:06 +00:00
Ok ( ( self , msg ) )
}
2023-11-09 10:19:43 +00:00
pub fn revert_staged_changes ( mut self ) -> Result < Self , Error > {
self . staging . update ( LayoutStaging {
parameters : Lww ::new ( self . current ( ) . parameters . clone ( ) ) ,
roles : LwwMap ::new ( ) ,
} ) ;
2023-11-08 16:49:06 +00:00
Ok ( self )
}
pub fn check ( & self ) -> Result < ( ) , String > {
2023-11-11 12:10:59 +00:00
// TODO: anything more ?
2023-11-16 12:26:43 +00:00
self . current ( ) . check ( )
2023-11-08 16:49:06 +00:00
}
}