Skip to content

Commit 0fe6a1e

Browse files
authored
Merge pull request #106 from d-Rickyy-b/static-ct-backoff
fix(static-ct): defer partial tile fetching
2 parents 27bb5c6 + 9b3b093 commit 0fe6a1e

1 file changed

Lines changed: 52 additions & 7 deletions

File tree

internal/certificatetransparency/ct-tiled.go

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,24 @@ func ConvertTileLeafToRawLogEntry(leaf TileLeaf, index uint64) *ct.RawLogEntry {
225225
return rawEntry
226226
}
227227

228+
// DefaultMaxPartialWait is the default maximum time to wait before forcefully
229+
// fetching a partial tile that has not yet grown into a full tile.
230+
const DefaultMaxPartialWait = 1 * time.Minute
231+
228232
type StaticCTClient struct {
229233
url string
230234
httpClient *http.Client
231235
backoff backoff.Backoff
232236
userAgent string
233237
ctIndex uint64
238+
239+
// Deferred partial-tile state.
240+
// partialTileIndex holds the tile index of the currently tracked partial tile.
241+
// partialTileFirstSeen is when that partial tile was first observed; zero means
242+
// no partial tile is being tracked.
243+
partialTileIndex uint64
244+
partialTileFirstSeen time.Time
245+
maxPartialWait time.Duration
234246
}
235247

236248
func NewStaticCTClient(url string, httpClient *http.Client, userAgent string, startIndex uint64) *StaticCTClient {
@@ -243,8 +255,9 @@ func NewStaticCTClient(url string, httpClient *http.Client, userAgent string, st
243255
Factor: 1.3,
244256
Jitter: true,
245257
},
246-
userAgent: userAgent,
247-
ctIndex: startIndex,
258+
userAgent: userAgent,
259+
ctIndex: startIndex,
260+
maxPartialWait: DefaultMaxPartialWait,
248261
}
249262
}
250263

@@ -296,22 +309,54 @@ func (s *StaticCTClient) fetchAndProcessTiles(ctx context.Context, foundCert fun
296309
endTile := currentTreeSize / TileSize
297310

298311
// Process full tiles
312+
fetchedFullTiles := false
299313
for tileIndex := startTile; tileIndex < endTile; tileIndex++ {
300314
if err := s.processTile(ctx, tileIndex, 0, foundCert, foundPrecert); err != nil {
301315
return false, fmt.Errorf("processing tile %d: %w", tileIndex, err)
302316
}
317+
318+
fetchedFullTiles = true
319+
}
320+
321+
// When the current end tile has advanced past the tracked partial tile, that tile
322+
// has since become a full tile and been processed; reset tracking so we start
323+
// fresh for the new partial tile (if any).
324+
if endTile > s.partialTileIndex {
325+
s.partialTileFirstSeen = time.Time{}
303326
}
304327

305-
// Process partial tile if exists
328+
// Process partial tiles.
306329
partialSize := currentTreeSize % TileSize
307330
if partialSize > 0 {
308-
if err := s.processTile(ctx, endTile, partialSize, foundCert, foundPrecert); err != nil {
309-
log.Printf("Warning: error processing partial tile %d: %s\n", endTile, err)
310-
// Don't return error for partial tiles as they might be incomplete
331+
switch {
332+
case s.partialTileFirstSeen.IsZero() || s.partialTileIndex != endTile:
333+
// First time we see this partial tile – start the deferral clock.
334+
s.partialTileIndex = endTile
335+
s.partialTileFirstSeen = time.Now()
336+
log.Println("Deferring fetch of partial tile", endTile, "with size", partialSize)
337+
338+
case time.Since(s.partialTileFirstSeen) >= s.maxPartialWait:
339+
// The partial tile has been pending too long – fetch it now to prevent
340+
// extreme processing delays on slow-growing logs.
341+
log.Println("Forcefully fetching partial tile", endTile, "with size", partialSize)
342+
343+
if err := s.processTile(ctx, endTile, partialSize, foundCert, foundPrecert); err != nil {
344+
log.Printf("Warning: error processing partial tile %d: %s\n", endTile, err)
345+
}
346+
347+
// Reset tracking; the tile will be re-observed on the next poll if it
348+
// still hasn't grown into a full tile.
349+
s.partialTileFirstSeen = time.Time{}
350+
351+
default:
352+
// Still within the deferral window – skip.
311353
}
354+
} else {
355+
// currentTreeSize is an exact multiple of TileSize; no partial tile exists.
356+
s.partialTileFirstSeen = time.Time{}
312357
}
313358

314-
return true, nil
359+
return fetchedFullTiles, nil
315360
}
316361

317362
// processTile processes a single tile from the tiled log.

0 commit comments

Comments
 (0)