Skip to content

Commit 2c7d7b7

Browse files
committed
refactor(pullsync): replace hasValidationError flag with two-bucket error accumulators
The hasValidationError bool was set at six separate sites and acted on in a deferred block at the end of Sync(). This is an outlier in the bee codebase, where the standard pattern is to use error accumulators whose nil-ness is the condition. Replace with two accumulators that express the existing semantic distinction structurally: chunkErr — stamp, solicitation, and structural failures. A non-nil chunkErr zeros topmost, preventing interval advancement past BinIDs whose chunks were never stored. overwriteErr — ErrOverwriteNewerChunk only. The chunk is already present in the reserve; advancing the interval past it is correct, so overwriteErr does not affect topmost. Both accumulators are joined on return, preserving the caller contract: all errors remain reachable via errors.Is. No interface change. No behaviour change.
1 parent 4f9650f commit 2c7d7b7

1 file changed

Lines changed: 13 additions & 23 deletions

File tree

pkg/pullsync/pullsync.go

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -322,14 +322,20 @@ func (s *Syncer) Sync(ctx context.Context, peer swarm.Address, bin uint8, start
322322

323323
chunksToPut := make([]swarm.Chunk, 0, ctr)
324324

325+
// chunkErr accumulates validation failures (stamp, solicitation, structural).
326+
// A non-nil chunkErr zeros topmost, preventing interval advancement past
327+
// BinIDs whose chunks were never stored.
328+
// overwriteErr accumulates ErrOverwriteNewerChunk: the chunk is already
329+
// present in the reserve, so advancing the interval past it is correct.
330+
// Both are joined on return so callers can inspect individual errors via errors.Is.
325331
var (
326-
chunkErr error
327-
hasValidationError bool // true when a chunk was not stored due to a validation failure
332+
chunkErr error
333+
overwriteErr error
328334
)
329335
for ; ctr > 0; ctr-- {
330336
var delivery pb.Delivery
331337
if err = r.ReadMsgWithContext(ctx, &delivery); err != nil {
332-
return 0, 0, errors.Join(chunkErr, fmt.Errorf("read delivery: %w", err))
338+
return 0, 0, errors.Join(chunkErr, overwriteErr, fmt.Errorf("read delivery: %w", err))
333339
}
334340

335341
addr := swarm.NewAddress(delivery.Address)
@@ -344,21 +350,18 @@ func (s *Syncer) Sync(ctx context.Context, peer swarm.Address, bin uint8, start
344350
stamp := new(postage.Stamp)
345351
if err = stamp.UnmarshalBinary(delivery.Stamp); err != nil {
346352
chunkErr = errors.Join(chunkErr, err)
347-
hasValidationError = true
348353
continue
349354
}
350355
stampHash, err := stamp.Hash()
351356
if err != nil {
352357
chunkErr = errors.Join(chunkErr, err)
353-
hasValidationError = true
354358
continue
355359
}
356360

357361
wantChunkID := addr.ByteString() + string(stamp.BatchID()) + string(stampHash)
358362
if _, ok := wantChunks[wantChunkID]; !ok {
359363
s.logger.Debug("want chunks", "error", ErrUnsolicitedChunk, "peer_address", peer, "chunk_address", addr)
360364
chunkErr = errors.Join(chunkErr, ErrUnsolicitedChunk)
361-
hasValidationError = true
362365
continue
363366
}
364367

@@ -368,7 +371,6 @@ func (s *Syncer) Sync(ctx context.Context, peer swarm.Address, bin uint8, start
368371
if err != nil {
369372
s.logger.Debug("unverified stamp", "error", err, "peer_address", peer, "chunk_address", newChunk)
370373
chunkErr = errors.Join(chunkErr, err)
371-
hasValidationError = true
372374
continue
373375
}
374376

@@ -378,15 +380,13 @@ func (s *Syncer) Sync(ctx context.Context, peer swarm.Address, bin uint8, start
378380
addr, err := chunk.Address()
379381
if err != nil {
380382
chunkErr = errors.Join(chunkErr, err)
381-
hasValidationError = true
382383
continue
383384
}
384385
s.logger.Debug("sync gsoc", "peer_address", peer, "chunk_address", addr, "wrapped_chunk_address", chunk.WrappedChunk().Address())
385386
s.gsocHandler(chunk)
386387
} else {
387388
s.logger.Debug("invalid cac/soc chunk", "error", swarm.ErrInvalidChunk, "peer_address", peer, "chunk", chunk)
388389
chunkErr = errors.Join(chunkErr, swarm.ErrInvalidChunk)
389-
hasValidationError = true
390390
s.metrics.ReceivedInvalidChunk.Inc()
391391
continue
392392
}
@@ -401,32 +401,22 @@ func (s *Syncer) Sync(ctx context.Context, peer swarm.Address, bin uint8, start
401401

402402
for _, c := range chunksToPut {
403403
if err := s.store.ReservePutter().Put(ctx, c); err != nil {
404-
// in case of these errors, no new items are added to the storage, so it
405-
// is safe to continue with the next chunk
406404
if errors.Is(err, storage.ErrOverwriteNewerChunk) {
407405
s.logger.Debug("overwrite newer chunk", "error", err, "peer_address", peer, "chunk", c)
408-
chunkErr = errors.Join(chunkErr, err)
409-
// ErrOverwriteNewerChunk is not a validation failure: the chunk is
410-
// already present in the reserve with a newer stamp. It is safe to
411-
// advance the interval past it.
406+
overwriteErr = errors.Join(overwriteErr, err)
412407
continue
413408
}
414-
return 0, 0, errors.Join(chunkErr, err)
409+
return 0, 0, errors.Join(chunkErr, overwriteErr, err)
415410
}
416411
chunksPut++
417412
}
418413
}
419414

420-
// Zero topmost when any chunk failed validation (stamp, solicitation,
421-
// or structural check). This prevents the caller from advancing its
422-
// interval past BinIDs whose chunks were never stored.
423-
// ErrOverwriteNewerChunk does not zero topmost: the chunk is already
424-
// present in the reserve with a newer stamp, so advancement is safe.
425-
if hasValidationError {
415+
if chunkErr != nil {
426416
topmost = 0
427417
}
428418

429-
return topmost, chunksPut, chunkErr
419+
return topmost, chunksPut, errors.Join(chunkErr, overwriteErr)
430420
}
431421

432422
// makeOffer tries to assemble an offer for a given requested interval.

0 commit comments

Comments
 (0)