|
8 | 8 | "fmt" |
9 | 9 | "strconv" |
10 | 10 | "sync" |
| 11 | + "time" |
11 | 12 |
|
12 | 13 | ds "github.com/ipfs/go-datastore" |
13 | 14 | "github.com/ipfs/go-datastore/query" |
@@ -56,6 +57,8 @@ type BatchQueue struct { |
56 | 57 | // txSeen is an in-memory dedup set keyed by sha256 hash of each tx. |
57 | 58 | // hashes are added in AddBatch and removed on successful Ack. |
58 | 59 | // prevents the reaper from enqueuing the same tx multiple scrape cycles. |
| 60 | + // its size tracks queued + in-flight txs, so it is bounded indirectly |
| 61 | + // by maxQueueSize (unbounded when maxQueueSize is 0). |
59 | 62 | txSeen map[[32]byte]struct{} |
60 | 63 |
|
61 | 64 | // totalEnqueued counts batches ever enqueued via AddBatch. Monotonic, |
@@ -134,7 +137,7 @@ func (bq *BatchQueue) AddBatch(ctx context.Context, batch coresequencer.Batch) e |
134 | 137 | key := seqToKey(bq.nextAddSeq) |
135 | 138 | if err := bq.persistBatch(ctx, batch, key); err != nil { |
136 | 139 | bq.rollbackSeenLocked(hashes) |
137 | | - return err |
| 140 | + return fmt.Errorf("failed to persist batch %s to WAL: %w", key, err) |
138 | 141 | } |
139 | 142 | bq.nextAddSeq++ |
140 | 143 |
|
@@ -198,8 +201,11 @@ func (bq *BatchQueue) Drain(ctx context.Context, maxBytes uint64) (*coresequence |
198 | 201 | } |
199 | 202 |
|
200 | 203 | // SetPostponed records txs that should be requeued on the next Ack. |
201 | | -// Must be called between Drain and Ack. The queue owns this state so |
202 | | -// it is only cleared on successful Ack — no data loss on failure. |
| 204 | +// Must be called at most once between a Drain and its Ack — a later call in |
| 205 | +// the same cycle would overwrite the recorded txs. The postponedItem guard |
| 206 | +// makes a repeated call after a failed Ack a no-op, so the already-persisted |
| 207 | +// entry is not replaced. The queue owns this state so it is only cleared on |
| 208 | +// successful Ack — no data loss on failure. |
203 | 209 | func (bq *BatchQueue) SetPostponed(txs [][]byte) { |
204 | 210 | bq.mu.Lock() |
205 | 211 | defer bq.mu.Unlock() |
@@ -305,7 +311,11 @@ func (bq *BatchQueue) rollbackInFlightLocked(ctx context.Context) { |
305 | 311 | } |
306 | 312 |
|
307 | 313 | if bq.postponedItem != nil { |
308 | | - if err := bq.db.Delete(ctx, ds.NewKey(bq.postponedItem.Key)); err != nil { |
| 314 | + // detach from the caller's context so this best-effort cleanup still |
| 315 | + // runs during graceful shutdown when the drain context is cancelled |
| 316 | + delCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) |
| 317 | + defer cancel() |
| 318 | + if err := bq.db.Delete(delCtx, ds.NewKey(bq.postponedItem.Key)); err != nil { |
309 | 319 | bq.logger.Warn().Err(err).Str("key", bq.postponedItem.Key). |
310 | 320 | Msg("failed to delete rolled-back postponed WAL entry") |
311 | 321 | } |
@@ -395,6 +405,9 @@ func (bq *BatchQueue) DropIncluded(ctx context.Context, included [][]byte) (int, |
395 | 405 | } |
396 | 406 |
|
397 | 407 | var dropped int |
| 408 | + // in-place compaction: kept aliases bq.queue's backing array. This is safe |
| 409 | + // because range evaluates the slice header once and writes always trail |
| 410 | + // reads (at most one item is appended per item iterated). |
398 | 411 | kept := bq.queue[:bq.head] |
399 | 412 | for _, item := range bq.queue[bq.head:] { |
400 | 413 | remaining := make([][]byte, 0, len(item.Batch.Transactions)) |
@@ -456,8 +469,9 @@ func (bq *BatchQueue) Load(ctx context.Context) error { |
456 | 469 | var legacyItems []queuedItem |
457 | 470 | for result := range results.Next() { |
458 | 471 | if result.Error != nil { |
459 | | - bq.logger.Error().Err(result.Error).Msg("failed to read entry from datastore") |
460 | | - continue |
| 472 | + // a datastore read failure means the WAL cannot be trusted as |
| 473 | + // loaded — fail startup rather than silently dropping txs. |
| 474 | + return fmt.Errorf("failed to read WAL entry from datastore: %w", result.Error) |
461 | 475 | } |
462 | 476 | // We care about the last part of the key (the sequence number) |
463 | 477 | // ds.Key usually has a leading slash. |
|
0 commit comments