@@ -720,9 +720,12 @@ impl Timeline {
720
720
"inconsistent v1/v2 reldir keyspace for rel {}: v1_exists={}, v2_exists={}" ,
721
721
tag,
722
722
v1_exists,
723
- v2_exists
723
+ v2_exists,
724
724
) ;
725
725
}
726
+ Err ( e) if e. is_cancel ( ) => {
727
+ // Cancellation errors are fine to ignore, do not log.
728
+ }
726
729
Err ( e) => {
727
730
tracing:: warn!( "failed to get rel exists in v2: {e}" ) ;
728
731
}
@@ -811,11 +814,11 @@ impl Timeline {
811
814
forknum : key. field5 ,
812
815
} ;
813
816
if val == RelDirExists :: Removed {
814
- debug_assert ! ( !rels. contains( & tag) , "removed reltag in v2" ) ;
817
+ debug_assert ! ( !rels. contains( & tag) , "removed reltag in v2: {tag} " ) ;
815
818
continue ;
816
819
}
817
820
let did_not_contain = rels. insert ( tag) ;
818
- debug_assert ! ( did_not_contain, "duplicate reltag in v2" ) ;
821
+ debug_assert ! ( did_not_contain, "duplicate reltag in v2: {tag} " ) ;
819
822
}
820
823
Ok ( rels)
821
824
}
@@ -863,6 +866,9 @@ impl Timeline {
863
866
rels_v2. len( )
864
867
) ;
865
868
}
869
+ Err ( e) if e. is_cancel ( ) => {
870
+ // Cancellation errors are fine to ignore, do not log.
871
+ }
866
872
Err ( e) => {
867
873
tracing:: warn!( "failed to list rels in v2: {e}" ) ;
868
874
}
@@ -1724,6 +1730,8 @@ pub struct RelDirMode {
1724
1730
current_status : RelSizeMigration ,
1725
1731
// Whether we should initialize the v2 keyspace or not.
1726
1732
initialize : bool ,
1733
+ // Whether we should disable v1 access starting this LSNor not
1734
+ disable_v1 : bool ,
1727
1735
}
1728
1736
1729
1737
impl DatadirModification < ' _ > {
@@ -2085,44 +2093,82 @@ impl DatadirModification<'_> {
2085
2093
///
2086
2094
/// As this function is only used on the write path, we do not need to read the migrated_at
2087
2095
/// field.
2088
- pub fn maybe_enable_rel_size_v2 ( & mut self , is_create : bool ) -> anyhow:: Result < RelDirMode > {
2096
+ pub ( crate ) fn maybe_enable_rel_size_v2 (
2097
+ & mut self ,
2098
+ lsn : Lsn ,
2099
+ is_create : bool ,
2100
+ ) -> anyhow:: Result < RelDirMode > {
2089
2101
// TODO: define the behavior of the tenant-level config flag and use feature flag to enable this feature
2102
+ let expected_status = self . tline . get_rel_size_v2_expected_state ( ) ;
2103
+ let ( persistent_status, migrated_at) = self . tline . get_rel_size_v2_status ( ) ;
2104
+
2105
+ // migrated_at LSN means the LSN where we "enable_v2" (but not "disable_v1")
2106
+ if let Some ( migrated_at) = migrated_at
2107
+ && lsn <= migrated_at
2108
+ && persistent_status == RelSizeMigration :: Migrating
2109
+ {
2110
+ // Revert the status to legacy if the write head LSN is before the migrating LSN.
2111
+ self . tline
2112
+ . update_rel_size_v2_status ( RelSizeMigration :: Legacy , None ) ?;
2113
+ }
2114
+ // TODO: what if the migration is at "migrated" state but we need to revert?
2115
+
2116
+ // Only initialize the v2 keyspace on new relation creation. No initialization
2117
+ // during `timeline_create` (TODO: fix this, we should allow, but currently it
2118
+ // hits expected consistency issues; need to ignore warnings).
2119
+ let can_update = is_create && !self . is_importing_pgdata ;
2090
2120
2091
- let ( status, _) = self . tline . get_rel_size_v2_status ( ) ;
2092
- let config = self . tline . get_rel_size_v2_enabled ( ) ;
2093
- match ( config, status) {
2094
- ( false , RelSizeMigration :: Legacy ) => {
2121
+ match ( expected_status, persistent_status) {
2122
+ ( RelSizeMigration :: Legacy , RelSizeMigration :: Legacy ) => {
2095
2123
// tenant config didn't enable it and we didn't write any reldir_v2 key yet
2096
2124
Ok ( RelDirMode {
2097
2125
current_status : RelSizeMigration :: Legacy ,
2098
2126
initialize : false ,
2127
+ disable_v1 : false ,
2099
2128
} )
2100
2129
}
2101
- ( false , status @ RelSizeMigration :: Migrating | status @ RelSizeMigration :: Migrated ) => {
2102
- // index_part already persisted that the timeline has enabled rel_size_v2
2130
+ (
2131
+ RelSizeMigration :: Legacy ,
2132
+ current_status @ RelSizeMigration :: Migrating
2133
+ | current_status @ RelSizeMigration :: Migrated ,
2134
+ ) => {
2135
+ // already persisted that the timeline has enabled rel_size_v2, cannot rollback
2103
2136
Ok ( RelDirMode {
2104
- current_status : status ,
2137
+ current_status,
2105
2138
initialize : false ,
2139
+ disable_v1 : false ,
2106
2140
} )
2107
2141
}
2108
- ( true , RelSizeMigration :: Legacy ) => {
2142
+ (
2143
+ expected_status @ RelSizeMigration :: Migrating
2144
+ | expected_status @ RelSizeMigration :: Migrated ,
2145
+ RelSizeMigration :: Legacy ,
2146
+ ) => {
2109
2147
// The first time we enable it, we need to persist it in `index_part.json`
2110
2148
// The caller should update the reldir status once the initialization is done.
2111
- //
2112
- // Only initialize the v2 keyspace on new relation creation. No initialization
2113
- // during `timeline_create` (TODO: fix this, we should allow, but currently it
2114
- // hits consistency issues).
2149
+
2115
2150
Ok ( RelDirMode {
2116
2151
current_status : RelSizeMigration :: Legacy ,
2117
- initialize : is_create && !self . is_importing_pgdata ,
2152
+ initialize : can_update,
2153
+ disable_v1 : can_update && expected_status == RelSizeMigration :: Migrated ,
2118
2154
} )
2119
2155
}
2120
- ( true , status @ RelSizeMigration :: Migrating | status @ RelSizeMigration :: Migrated ) => {
2121
- // index_part already persisted that the timeline has enabled rel_size_v2
2122
- // and we don't need to do anything
2156
+ ( RelSizeMigration :: Migrating , current_status @ RelSizeMigration :: Migrating )
2157
+ | ( RelSizeMigration :: Migrated , current_status @ RelSizeMigration :: Migrated )
2158
+ | ( RelSizeMigration :: Migrating , current_status @ RelSizeMigration :: Migrated ) => {
2159
+ // Keep the current state
2123
2160
Ok ( RelDirMode {
2124
- current_status : status ,
2161
+ current_status,
2125
2162
initialize : false ,
2163
+ disable_v1 : false ,
2164
+ } )
2165
+ }
2166
+ ( RelSizeMigration :: Migrated , RelSizeMigration :: Migrating ) => {
2167
+ // Switch to v2-only mode
2168
+ Ok ( RelDirMode {
2169
+ current_status : RelSizeMigration :: Migrating ,
2170
+ initialize : false ,
2171
+ disable_v1 : can_update,
2126
2172
} )
2127
2173
}
2128
2174
}
@@ -2137,8 +2183,8 @@ impl DatadirModification<'_> {
2137
2183
ctx : & RequestContext ,
2138
2184
) -> Result < ( ) , WalIngestError > {
2139
2185
let v2_mode = self
2140
- . maybe_enable_rel_size_v2 ( false )
2141
- . map_err ( WalIngestErrorKind :: MaybeRelSizeV2Error ) ?;
2186
+ . maybe_enable_rel_size_v2 ( self . lsn , false )
2187
+ . map_err ( WalIngestErrorKind :: RelSizeV2Error ) ?;
2142
2188
2143
2189
// Add it to the directory (if it doesn't exist already)
2144
2190
let buf = self . get ( DBDIR_KEY , ctx) . await ?;
@@ -2290,15 +2336,6 @@ impl DatadirModification<'_> {
2290
2336
sparse_rel_dir_key,
2291
2337
Value :: Image ( RelDirExists :: Exists . encode ( ) ) ,
2292
2338
) ;
2293
- tracing:: info!(
2294
- "migrated rel_size_v2: {}" ,
2295
- RelTag {
2296
- spcnode,
2297
- dbnode,
2298
- relnode,
2299
- forknum
2300
- }
2301
- ) ;
2302
2339
rel_cnt += 1 ;
2303
2340
}
2304
2341
}
@@ -2307,9 +2344,6 @@ impl DatadirModification<'_> {
2307
2344
self . lsn,
2308
2345
rel_cnt
2309
2346
) ;
2310
- self . tline
2311
- . update_rel_size_v2_status ( RelSizeMigration :: Migrating , Some ( self . lsn ) )
2312
- . map_err ( WalIngestErrorKind :: MaybeRelSizeV2Error ) ?;
2313
2347
Ok :: < _ , WalIngestError > ( ( ) )
2314
2348
}
2315
2349
@@ -2392,35 +2426,50 @@ impl DatadirModification<'_> {
2392
2426
// It's possible that this is the first rel for this db in this
2393
2427
// tablespace. Create the reldir entry for it if so.
2394
2428
let mut dbdir = DbDirectory :: des ( & self . get ( DBDIR_KEY , ctx) . await ?) ?;
2429
+ let mut is_dbdir_dirty = false ;
2395
2430
2396
2431
let dbdir_exists =
2397
2432
if let hash_map:: Entry :: Vacant ( e) = dbdir. dbdirs . entry ( ( rel. spcnode , rel. dbnode ) ) {
2398
2433
// Didn't exist. Update dbdir
2399
2434
e. insert ( false ) ;
2400
- let buf = DbDirectory :: ser ( & dbdir) ?;
2401
2435
self . pending_directory_entries . push ( (
2402
2436
DirectoryKind :: Db ,
2403
2437
MetricsUpdate :: Set ( dbdir. dbdirs . len ( ) as u64 ) ,
2404
2438
) ) ;
2405
- self . put ( DBDIR_KEY , Value :: Image ( buf . into ( ) ) ) ;
2439
+ is_dbdir_dirty = true ;
2406
2440
false
2407
2441
} else {
2408
2442
true
2409
2443
} ;
2410
2444
2411
2445
let mut v2_mode = self
2412
- . maybe_enable_rel_size_v2 ( true )
2413
- . map_err ( WalIngestErrorKind :: MaybeRelSizeV2Error ) ?;
2446
+ . maybe_enable_rel_size_v2 ( self . lsn , true )
2447
+ . map_err ( WalIngestErrorKind :: RelSizeV2Error ) ?;
2414
2448
2415
2449
if v2_mode. initialize {
2416
2450
if let Err ( e) = self . initialize_rel_size_v2_keyspace ( ctx, & dbdir) . await {
2417
2451
tracing:: warn!( "error initializing rel_size_v2 keyspace: {}" , e) ;
2418
2452
// TODO: circuit breaker so that it won't retry forever
2419
2453
} else {
2420
2454
v2_mode. current_status = RelSizeMigration :: Migrating ;
2455
+ self . tline
2456
+ . update_rel_size_v2_status ( RelSizeMigration :: Migrating , Some ( self . lsn ) )
2457
+ . map_err ( WalIngestErrorKind :: RelSizeV2Error ) ?;
2421
2458
}
2422
2459
}
2423
2460
2461
+ if v2_mode. disable_v1 {
2462
+ v2_mode. current_status = RelSizeMigration :: Migrated ;
2463
+ self . tline
2464
+ . update_rel_size_v2_status ( RelSizeMigration :: Migrated , Some ( self . lsn ) )
2465
+ . map_err ( WalIngestErrorKind :: RelSizeV2Error ) ?;
2466
+ }
2467
+
2468
+ if is_dbdir_dirty {
2469
+ let buf = DbDirectory :: ser ( & dbdir) ?;
2470
+ self . put ( DBDIR_KEY , Value :: Image ( buf. into ( ) ) ) ;
2471
+ }
2472
+
2424
2473
if v2_mode. current_status != RelSizeMigration :: Migrated {
2425
2474
self . put_rel_creation_v1 ( rel, dbdir_exists, ctx) . await ?;
2426
2475
}
@@ -2581,8 +2630,8 @@ impl DatadirModification<'_> {
2581
2630
ctx : & RequestContext ,
2582
2631
) -> Result < ( ) , WalIngestError > {
2583
2632
let v2_mode = self
2584
- . maybe_enable_rel_size_v2 ( false )
2585
- . map_err ( WalIngestErrorKind :: MaybeRelSizeV2Error ) ?;
2633
+ . maybe_enable_rel_size_v2 ( self . lsn , false )
2634
+ . map_err ( WalIngestErrorKind :: RelSizeV2Error ) ?;
2586
2635
match v2_mode. current_status {
2587
2636
RelSizeMigration :: Legacy => {
2588
2637
self . put_rel_drop_v1 ( drop_relations, ctx) . await ?;
@@ -2600,6 +2649,12 @@ impl DatadirModification<'_> {
2600
2649
) ;
2601
2650
}
2602
2651
}
2652
+ Err ( WalIngestError {
2653
+ kind : WalIngestErrorKind :: Cancelled ,
2654
+ ..
2655
+ } ) => {
2656
+ // Cancellation errors are fine to ignore, do not log.
2657
+ }
2603
2658
Err ( e) => {
2604
2659
tracing:: warn!( "error dropping rels: {}" , e) ;
2605
2660
}
0 commit comments