@@ -31,6 +31,11 @@ import (
31
31
32
32
const IdealEndEpochBuffer = 2 * builtin .EpochsInDay
33
33
34
+ // assuming that snap takes up to 20min to get to submitting the message we want to avoid sectors from deadlines which will
35
+ // become immutable in the next 20min (40 epochs)
36
+ // NOTE: Don't set this value to more than one deadline (60 epochs)
37
+ var SnapImmutableDeadlineEpochsBuffer = abi .ChainEpoch (40 )
38
+
34
39
type PieceIngesterSnap struct {
35
40
ctx context.Context
36
41
db * harmonydb.DB
@@ -276,6 +281,25 @@ func (p *PieceIngesterSnap) AllocatePieceToSector(ctx context.Context, maddr add
276
281
}
277
282
}
278
283
284
+ // non-mutable deadline is the current deadline and the next one. Doesn't matter if the current one was proven or not.
285
+
286
+ curDeadline , err := p .api .StateMinerProvingDeadline (ctx , p .miner , types .EmptyTSK )
287
+ if err != nil {
288
+ return api.SectorOffset {}, xerrors .Errorf ("getting proving deadline: %w" , err )
289
+ }
290
+
291
+ dlIdxImmutableCur := curDeadline .Index
292
+ dlIdxImmutableNext := (curDeadline .Index + 1 ) % curDeadline .WPoStPeriodDeadlines
293
+
294
+ // The deadline which might become mutable soon
295
+ dlIdxImmutableNextNext := dlIdxImmutableNext
296
+ epochsInDeadline := curDeadline .CurrentEpoch - curDeadline .Open // how many into the deadline we are
297
+ epochsPerDeadline := curDeadline .WPoStProvingPeriod / abi .ChainEpoch (curDeadline .WPoStPeriodDeadlines )
298
+
299
+ if epochsInDeadline >= epochsPerDeadline - SnapImmutableDeadlineEpochsBuffer {
300
+ dlIdxImmutableNextNext = (dlIdxImmutableNext + 1 ) % curDeadline .WPoStPeriodDeadlines
301
+ }
302
+
279
303
// Allocation to open sector failed, create a new sector and add the piece to it
280
304
281
305
// TX
@@ -291,15 +315,15 @@ func (p *PieceIngesterSnap) AllocatePieceToSector(ctx context.Context, maddr add
291
315
292
316
var num * int64
293
317
_ , err = p .db .BeginTransaction (ctx , func (tx * harmonydb.Tx ) (commit bool , err error ) {
294
- var candidates [] struct {
318
+ type CandidateSector struct {
295
319
Sector int64 `db:"sector_num"`
296
320
Expiration int64 `db:"expiration_epoch"`
297
321
}
298
322
299
323
// maxExpiration = maybe(max sector expiration_epoch)
300
324
// minExpiration = piece.DealSchedule.EndEpoch
301
325
// ideal expiration = minExpiration + 2 days
302
- err = tx .Select ( & candidates , `
326
+ rows , err : = tx .Query ( `
303
327
SELECT sm.sector_num, sm.expiration_epoch
304
328
FROM sectors_meta sm
305
329
LEFT JOIN sectors_snap_pipeline ssp on sm.sp_id = ssp.sp_id and sm.sector_num = ssp.sector_number
@@ -309,28 +333,83 @@ func (p *PieceIngesterSnap) AllocatePieceToSector(ctx context.Context, maddr add
309
333
AND sm.expiration_epoch IS NOT NULL
310
334
AND sm.expiration_epoch > $1
311
335
AND ($2 = 0 OR sm.expiration_epoch < $2)
336
+ AND deadline IS NOT NULL AND deadline NOT IN ($5, $6, $7)
312
337
ORDER BY ABS(sm.expiration_epoch - ($1 + $3))
313
- LIMIT 10
314
- ` , int64 (piece .DealSchedule .EndEpoch ), maxExpiration , IdealEndEpochBuffer , p .mid )
338
+ ` , int64 (piece .DealSchedule .EndEpoch ), maxExpiration , IdealEndEpochBuffer , p .mid , dlIdxImmutableCur , dlIdxImmutableNext , dlIdxImmutableNextNext )
315
339
if err != nil {
316
340
return false , xerrors .Errorf ("allocating sector numbers: %w" , err )
317
341
}
342
+ defer rows .Close ()
318
343
319
- if len (candidates ) == 0 {
344
+ deadlineCache := map [uint64 ][]api.Partition {}
345
+ var tried int
346
+ var bestCandidate * CandidateSector
347
+
348
+ for rows .Next () {
349
+ var candidate CandidateSector
350
+ err := rows .Scan (& candidate .Sector , & candidate .Expiration )
351
+ if err != nil {
352
+ return false , xerrors .Errorf ("scanning row: %w" , err )
353
+ }
354
+ tried ++
355
+
356
+ sloc , err := p .api .StateSectorPartition (ctx , p .miner , abi .SectorNumber (candidate .Sector ), types .EmptyTSK )
357
+ if err != nil {
358
+ return false , xerrors .Errorf ("getting sector locations: %w" , err )
359
+ }
360
+
361
+ if _ , ok := deadlineCache [sloc .Deadline ]; ! ok {
362
+ dls , err := p .api .StateMinerPartitions (ctx , p .miner , sloc .Deadline , types .EmptyTSK )
363
+ if err != nil {
364
+ return false , xerrors .Errorf ("getting partitions: %w" , err )
365
+ }
366
+
367
+ deadlineCache [sloc .Deadline ] = dls
368
+ }
369
+
370
+ dl := deadlineCache [sloc .Deadline ]
371
+ if len (dl ) <= int (sloc .Partition ) {
372
+ return false , xerrors .Errorf ("partition %d not found in deadline %d" , sloc .Partition , sloc .Deadline )
373
+ }
374
+ part := dl [sloc .Partition ]
375
+
376
+ active , err := part .ActiveSectors .IsSet (uint64 (candidate .Sector ))
377
+ if err != nil {
378
+ return false , xerrors .Errorf ("checking active sectors: %w" , err )
379
+ }
380
+ if ! active {
381
+ live , err1 := part .LiveSectors .IsSet (uint64 (candidate .Sector ))
382
+ faulty , err2 := part .FaultySectors .IsSet (uint64 (candidate .Sector ))
383
+ recovering , err3 := part .RecoveringSectors .IsSet (uint64 (candidate .Sector ))
384
+ if err1 != nil || err2 != nil || err3 != nil {
385
+ return false , xerrors .Errorf ("checking sector status: %w, %w, %w" , err1 , err2 , err3 )
386
+ }
387
+
388
+ log .Debugw ("sector not active, skipping" , "sector" , candidate .Sector , "live" , live , "faulty" , faulty , "recovering" , recovering )
389
+ continue
390
+ }
391
+
392
+ bestCandidate = & candidate
393
+ break
394
+ }
395
+
396
+ if err := rows .Err (); err != nil {
397
+ return false , xerrors .Errorf ("iterating rows: %w" , err )
398
+ }
399
+
400
+ rows .Close ()
401
+
402
+ if bestCandidate == nil {
320
403
minEpoch := piece .DealSchedule .EndEpoch
321
404
maxEpoch := abi .ChainEpoch (maxExpiration )
322
405
323
406
minEpochDays := (minEpoch - head .Height ()) / builtin .EpochsInDay
324
407
maxEpochDays := (maxEpoch - head .Height ()) / builtin .EpochsInDay
325
408
326
- return false , xerrors .Errorf ("no suitable sectors found, minEpoch: %d, maxEpoch: %d, minExpirationDays: %d, maxExpirationDays: %d" , minEpoch , maxEpoch , minEpochDays , maxEpochDays )
409
+ return false , xerrors .Errorf ("no suitable sectors found, minEpoch: %d, maxEpoch: %d, minExpirationDays: %d, maxExpirationDays: %d (avoiding deadlines %d,%d,%d) " , minEpoch , maxEpoch , minEpochDays , maxEpochDays , dlIdxImmutableCur , dlIdxImmutableNext , dlIdxImmutableNextNext )
327
410
}
328
411
329
- // todo - nice to have:
330
- // * check sector liveness
331
- // * check deadline mutable
332
-
333
- candidate := candidates [0 ] // this one works best
412
+ candidate := * bestCandidate
334
413
335
414
si , err := p .api .StateSectorGetInfo (ctx , p .miner , abi .SectorNumber (candidate .Sector ), types .EmptyTSK )
336
415
if err != nil {
@@ -356,6 +435,7 @@ func (p *PieceIngesterSnap) AllocatePieceToSector(ctx context.Context, maddr add
356
435
"dealStartEpoch" , piece .DealSchedule .StartEpoch ,
357
436
"dealEndEpoch" , piece .DealSchedule .EndEpoch ,
358
437
"maxExpiration" , maxExpiration ,
438
+ "avoidingDeadlines" , []int {int (dlIdxImmutableCur ), int (dlIdxImmutableNext ), int (dlIdxImmutableNextNext )},
359
439
)
360
440
361
441
_ , err = tx .Exec (`SELECT insert_snap_ddo_piece($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` ,
0 commit comments