Skip to content

Commit 01a8ff6

Browse files
committed
fixup!: load diff info from blocks if not exists in queue state
1 parent 809b4fb commit 01a8ff6

5 files changed

Lines changed: 313 additions & 202 deletions

File tree

cli/src/node/mod.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ use tycho_collator::manager::CollationManager;
1414
use tycho_collator::mempool::{
1515
MempoolAdapter, MempoolAdapterSingleNodeImpl, MempoolAdapterStdImpl,
1616
};
17-
use tycho_collator::queue_adapter::{MessageQueueAdapter, MessageQueueAdapterStdImpl};
18-
use tycho_collator::state_node::{CollatorSyncContext, StateNodeAdapter, StateNodeAdapterStdImpl};
17+
use tycho_collator::queue_adapter::{MessageQueueAdapterAsync, MessageQueueAdapterStdImpl};
18+
use tycho_collator::state_node::{
19+
CollatorSyncContext, DiffLoader, StateNodeAdapter, StateNodeAdapterStdImpl,
20+
};
1921
use tycho_collator::types::CollatorConfig;
2022
use tycho_collator::validator::{
2123
ValidatorNetworkContext, ValidatorStdImpl, ValidatorStdImplConfig,
@@ -216,7 +218,15 @@ impl Node {
216218
};
217219
let queue = queue_factory.create()?;
218220
let message_queue_adapter = MessageQueueAdapterStdImpl::new(queue);
219-
message_queue_adapter.recover_after_restart(&mc_state)?;
221+
message_queue_adapter
222+
.recover_after_restart(
223+
&mc_state,
224+
DiffLoader::new(
225+
base.core_storage.block_handle_storage(),
226+
base.core_storage.block_storage(),
227+
),
228+
)
229+
.await?;
220230

221231
let validator = ValidatorStdImpl::new(
222232
ValidatorNetworkContext {

collator/src/internal_queue/queue.rs

Lines changed: 51 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ pub trait Queue<V>: Send
8888
where
8989
V: InternalMessageValue + Send + Sync,
9090
{
91+
fn zerostate_id(&self) -> ZerostateId;
92+
9193
/// Create iterator for specified shard and return it
9294
fn iterator(
9395
&self,
@@ -124,13 +126,16 @@ where
124126
partitions: &FastHashSet<QueuePartitionIdx>,
125127
) -> Result<()>;
126128

127-
/// Roll back commit pointers to the specified top blocks.
129+
/// Roll back commit pointers to the specified ones.
128130
fn rollback_commit_pointers(
129131
&self,
130132
to_mc_block_id: &BlockId,
131-
to_top_blocks: &[TopBlockIdUpdated],
133+
to_commit_pointers: FastHashMap<ShardIdent, (Option<QueueKey>, u32)>,
132134
) -> Result<Vec<ShardIdent>>;
133135

136+
/// Clear commit pointers and last committed mc block id.
137+
fn clear_commit_pointers(&self) -> Result<Vec<ShardIdent>>;
138+
134139
/// Remove all data in uncommitted zone
135140
fn clear_uncommitted_state(
136141
&self,
@@ -208,6 +213,10 @@ where
208213
P: QueueState<V> + Send + Sync + 'static,
209214
V: InternalMessageValue + Send + Sync,
210215
{
216+
fn zerostate_id(&self) -> ZerostateId {
217+
self.zerostate_id
218+
}
219+
211220
fn iterator(
212221
&self,
213222
partition: QueuePartitionIdx,
@@ -443,97 +452,44 @@ where
443452

444453
fn rollback_commit_pointers(
445454
&self,
446-
to_mc_block_id: &BlockId,
447-
to_top_blocks: &[TopBlockIdUpdated],
455+
mc_block_id: &BlockId,
456+
to_commit_pointers: FastHashMap<ShardIdent, (Option<QueueKey>, u32)>,
448457
) -> Result<Vec<ShardIdent>> {
449458
let _global_write_guard = self.global_lock.write();
450459

451460
let old_commit_pointers = self.state.get_commit_pointers()?;
452-
let mut new_commit_pointers = FastHashMap::default();
453-
let mut clear_commit_state = false;
454-
455-
for item in to_top_blocks {
456-
let block_id = &item.block.block_id;
457-
458-
let diff = self
459-
.state
460-
.get_diff_info(&block_id.shard, block_id.seqno, DiffZone::Both)?;
461461

462-
let diff = match diff {
463-
None if item.updated && item.block.ref_by_mc_seqno > self.zerostate_id.seqno => {
464-
// SAFETY: In this recovery fallback, a missing updated target diff is treated as
465-
// an already GC-collected queue boundary. Since the target MC commit may not be
466-
// idempotently committable without its diff info, the whole queue commit state is
467-
// reset and the following clear_uncommitted_state call removes the remaining tails.
468-
tracing::warn!(
469-
target: tracing_targets::MQ,
470-
"Clearing all commit pointers after missing updated shard {} with mc ref {} (zerostate {}) \
471-
during rollback to seqno {}: target diff is missing and old commit pointers: {:?}",
472-
block_id.shard,
473-
item.block.ref_by_mc_seqno,
474-
self.zerostate_id.seqno,
475-
block_id.seqno,
476-
old_commit_pointers,
477-
);
478-
clear_commit_state = true;
479-
break;
462+
// build new commit pointers
463+
let mut new_commit_pointers = FastHashMap::default();
464+
for (shard_id, (max_message, seqno)) in to_commit_pointers {
465+
if let Some(max_message) = max_message {
466+
if new_commit_pointers
467+
.insert(shard_id, (max_message, seqno))
468+
.is_some()
469+
{
470+
bail!("Duplicate shard in rollback_commit_pointers: {}", shard_id);
480471
}
481-
None if !item.updated => {
482-
if let Some(old_pointer) = old_commit_pointers.get(&block_id.shard) {
483-
if old_pointer.seqno != block_id.seqno {
484-
// SAFETY: In this recovery fallback, a missing target diff is treated as an
485-
// already GC-collected queue boundary. Dropping this shard pointer lets the
486-
// following clear_uncommitted_state call remove the remaining ahead-of-applied
487-
// queue suffix for this shard.
488-
tracing::warn!(
489-
target: tracing_targets::MQ,
490-
"Dropping commit pointer for unchanged shard {} during rollback to seqno {}: \
491-
target diff is missing and old pointer seqno is {}",
492-
block_id.shard,
493-
block_id.seqno,
494-
old_pointer.seqno,
495-
);
496-
continue;
497-
}
498-
if new_commit_pointers
499-
.insert(block_id.shard, (old_pointer.queue_key, old_pointer.seqno))
500-
.is_some()
501-
{
502-
bail!(
503-
"Duplicate shard in rollback_commit_pointers: {}",
504-
block_id.shard
505-
);
506-
}
472+
} else {
473+
// check if missed mandatory commit pointers in rollback
474+
if let Some(old_pointer) = old_commit_pointers.get(&shard_id) {
475+
if old_pointer.seqno != seqno {
476+
bail!(
477+
"Cannot roll back commit pointer for shard {} on seqno {}: diff is missing",
478+
shard_id,
479+
seqno,
480+
);
481+
}
482+
if new_commit_pointers
483+
.insert(shard_id, (old_pointer.queue_key, old_pointer.seqno))
484+
.is_some()
485+
{
486+
bail!("Duplicate shard in rollback_commit_pointers: {}", shard_id);
507487
}
508-
continue;
509488
}
510-
None => continue,
511-
Some(diff) => diff,
512-
};
513-
514-
if new_commit_pointers
515-
.insert(block_id.shard, (diff.max_message, diff.seqno))
516-
.is_some()
517-
{
518-
bail!(
519-
"Duplicate shard in rollback_commit_pointers: {}",
520-
block_id.shard
521-
);
522489
}
523490
}
524491

525-
// fully clear commit state when unable to rollback
526-
if clear_commit_state {
527-
let removed_commit_pointer_shards: Vec<_> =
528-
old_commit_pointers.keys().copied().collect();
529-
tracing::debug!(target: tracing_targets::MQ,
530-
?removed_commit_pointer_shards,
531-
"rollback_commit_pointers: clear_commit_pointers",
532-
);
533-
self.state.clear_commit_pointers()?;
534-
return Ok(removed_commit_pointer_shards);
535-
}
536-
492+
// detect commit pointers that will be removed
537493
let removed_commit_pointer_shards: Vec<_> = old_commit_pointers
538494
.keys()
539495
.filter(|shard_id| !new_commit_pointers.contains_key(shard_id))
@@ -543,11 +499,22 @@ where
543499
tracing::debug!(target: tracing_targets::MQ,
544500
?new_commit_pointers,
545501
?removed_commit_pointer_shards,
546-
"rollback_commit_pointers",
502+
"rollback_commit_pointers: replace_commit_pointers",
547503
);
548504

549505
self.state
550-
.replace_commit_pointers(new_commit_pointers, to_mc_block_id)?;
506+
.replace_commit_pointers(new_commit_pointers, mc_block_id)?;
507+
508+
Ok(removed_commit_pointer_shards)
509+
}
510+
511+
fn clear_commit_pointers(&self) -> Result<Vec<ShardIdent>> {
512+
let _global_write_guard = self.global_lock.write();
513+
514+
let old_commit_pointers = self.state.get_commit_pointers()?;
515+
let removed_commit_pointer_shards: Vec<_> = old_commit_pointers.keys().copied().collect();
516+
517+
self.state.clear_commit_pointers()?;
551518

552519
Ok(removed_commit_pointer_shards)
553520
}

0 commit comments

Comments
 (0)