diff --git a/internal/ctlog/ctlog.go b/internal/ctlog/ctlog.go index 036185b..7a757e7 100644 --- a/internal/ctlog/ctlog.go +++ b/internal/ctlog/ctlog.go @@ -665,7 +665,16 @@ func (l *Log) sequence(ctx context.Context) error { l.inSequencing = p.byHash l.poolMu.Unlock() - return l.sequencePool(ctx, p) + err := l.sequencePool(ctx, p) + + // Once sequencePool returns, the entries are either in the deduplication + // cache or finalized with an error. In the latter case, we don't want + // a resubmit to deduplicate against the failed sequencing. + l.poolMu.Lock() + l.inSequencing = nil + l.poolMu.Unlock() + + return err } func (l *Log) sequencePool(ctx context.Context, p *pool) (err error) { diff --git a/internal/ctlog/ctlog_test.go b/internal/ctlog/ctlog_test.go index 37ce970..c24b9c8 100644 --- a/internal/ctlog/ctlog_test.go +++ b/internal/ctlog/ctlog_test.go @@ -291,6 +291,17 @@ func testDuplicates(t *testing.T, addWithSeed func(*testing.T, *TestLog, int64) if e22.Timestamp != e21.Timestamp { t.Errorf("got timestamp %d, expected %d", e22.Timestamp, e21.Timestamp) } + + // A failed sequencing immediately allows resubmission (i.e., the failed + // entry in the inSequencing pool is not picked up). + + tl.Config.Backend.(*MemoryBackend).UploadCallback = failStagingButPersist + addCertificateExpectFailureWithSeed(t, tl, 3) + fatalIfErr(t, tl.Log.Sequence()) + + tl.Config.Backend.(*MemoryBackend).UploadCallback = nil + addCertificateWithSeed(t, tl, 3) + fatalIfErr(t, tl.Log.Sequence()) } func TestReloadLog(t *testing.T) {