Skip to content

Commit 3c8e889

Browse files
authored
[VIndex] Cleaner Output Log usage (#58)
The Output Log was being updated every time the map was updated. Now it is skipped if no data has changed in the map. This uses in-memory state, so on restarting the server, a single duplicate may still be written. This also ensures that updates to the Output Log are performed within the same lock used to update the VIndex. This resolves a race condition that could allow Lookup to return a proof to a different index root. Used this opportunity to simply pass the Input Log checkpoint into `publish` instead of reading it from disk again.
1 parent ccfb436 commit 3c8e889

File tree

1 file changed

+16
-24
lines changed

1 file changed

+16
-24
lines changed

vindex/map.go

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ func (m *inputLogMapper) syncFromInputLog(ctx context.Context) error {
316316
return fmt.Errorf("failed to update state: %v", err)
317317
}
318318

319-
klog.Infof("synced WAL to size %d", cp.Size)
319+
klog.V(1).Infof("synced WAL to size %d", cp.Size)
320320
return nil
321321
}
322322

@@ -442,27 +442,10 @@ func (b *VerifiableIndex) Update(ctx context.Context) error {
442442
if err := b.mapper.syncFromInputLog(ctx); err != nil {
443443
return err
444444
}
445-
if err := b.buildMap(ctx, true); err != nil {
446-
return err
447-
}
448-
return b.publish(ctx)
445+
return b.buildMap(ctx, true)
449446
}
450447

451-
func (b *VerifiableIndex) publish(ctx context.Context) error {
452-
// Get the latest input log checkpoint the map was built from.
453-
// TODO(mhutchinson): Possibly just pass this in?
454-
inCp, inCloser, err := b.db.Get([]byte(dbLatestCheckpointKey))
455-
if err != nil {
456-
if err == pebble.ErrNotFound {
457-
// If the key isn't there then nothing to do.
458-
return nil
459-
}
460-
return fmt.Errorf("failed to read latest checkpoint: %v", err)
461-
}
462-
if err := inCloser.Close(); err != nil {
463-
return fmt.Errorf("failed to close: %v", err)
464-
}
465-
448+
func (b *VerifiableIndex) publish(ctx context.Context, inCp []byte) error {
466449
// Construct the leaf for the output log
467450
rootNode, err := b.vstore.Load(ctx, prefix.RootLabel)
468451
if err != nil {
@@ -516,8 +499,13 @@ func (b *VerifiableIndex) buildMap(ctx context.Context, updateIndex bool) error
516499
return fmt.Errorf("failed to parse checkpoint: %v", err)
517500
}
518501

519-
klog.V(1).Infof("buildMap [%d, %d): parsing WAL", b.servingSize, size)
520-
for i := b.servingSize; i < size; {
502+
from, to := b.servingSize, size
503+
if from == to {
504+
klog.V(1).Infof("buildMap [%d, %d): nothing to do", from, to)
505+
return nil
506+
}
507+
klog.V(1).Infof("buildMap [%d, %d): parsing WAL", from, to)
508+
for i := from; i < to; {
521509
select {
522510
case <-ctx.Done():
523511
return ctx.Err()
@@ -590,8 +578,12 @@ func (b *VerifiableIndex) buildMap(ctx context.Context, updateIndex bool) error
590578
durationTotal := time.Since(startWal)
591579

592580
b.servingSize = size
593-
klog.Infof("buildMap: total=%s (wal=%s, vindex=%s)", durationTotal, durationWal, durationVIndex)
594-
return nil
581+
klog.Infof("buildMap [%d, %d): total=%s (wal=%s, vindex=%s)", from, to, durationTotal, durationWal, durationVIndex)
582+
583+
// This publish occurs within the indexMu lock intentionally.
584+
// This allows Lookup to always assume that the last leaf in the Output Log is the
585+
// one that commits to the current state of the index.
586+
return b.publish(ctx, cpRaw)
595587
}
596588

597589
// checkpointUnsafe parses a checkpoint without performing any signature verification.

0 commit comments

Comments
 (0)