// snapshotAtOrBeforeSeq returns true if the snapshot represents a seq // number at or before the given seq number. func snapshotAtOrBeforeSeq(path string, ss moss.Snapshot, seqMaxKey []byte, seqMaxWant uint64) (bool, error) { // Equivalent of bleve.Index.GetInternal(seqMaxKey). v, err := ss.Get(seqMaxKey, moss.ReadOptions{}) if err != nil { return false, err } if v == nil { return false, nil } if len(v) != 8 { return false, fmt.Errorf("wrong len seqMaxKey: %s, v: %s", seqMaxKey, v) } seqMaxCurr := binary.BigEndian.Uint64(v[0:8]) log.Printf("pindex_bleve_rollback: examining snapshot, path: %s,"+ " seqMaxKey: %s, seqMaxCurr: %d, seqMaxWant: %d", path, seqMaxKey, seqMaxCurr, seqMaxWant) return seqMaxCurr <= seqMaxWant, nil }
// update() mutates this lower level store with latest data from the // given higher level moss.Snapshot and returns a new moss.Snapshot // that the higher level can use which represents this lower level // store. func (s *llStore) update(ssHigher moss.Snapshot, maxBatchSize uint64) ( ssLower moss.Snapshot, err error) { if ssHigher != nil { iter, err := ssHigher.StartIterator(nil, nil, moss.IteratorOptions{ IncludeDeletions: true, SkipLowerLevel: true, }) if err != nil { return nil, err } defer func() { err = iter.Close() if err != nil { s.logf("llStore iter.Close err: %v", err) } }() kvWriter, err := s.kvStore.Writer() if err != nil { return nil, err } defer func() { err = kvWriter.Close() if err != nil { s.logf("llStore kvWriter.Close err: %v", err) } }() batch := kvWriter.NewBatch() defer func() { if batch != nil { err = batch.Close() if err != nil { s.logf("llStore batch.Close err: %v", err) } } }() var readOptions moss.ReadOptions i := uint64(0) for { if i%1000000 == 0 { s.logf("llStore.update, i: %d", i) } ex, key, val, err := iter.CurrentEx() if err == moss.ErrIteratorDone { break } if err != nil { return nil, err } switch ex.Operation { case moss.OperationSet: batch.Set(key, val) case moss.OperationDel: batch.Delete(key) case moss.OperationMerge: val, err = ssHigher.Get(key, readOptions) if err != nil { return nil, err } if val != nil { batch.Set(key, val) } else { batch.Delete(key) } default: return nil, fmt.Errorf("moss store, update,"+ " unexpected operation, ex: %v", ex) } i++ err = iter.Next() if err == moss.ErrIteratorDone { break } if err != nil { return nil, err } if maxBatchSize > 0 && i%maxBatchSize == 0 { err = kvWriter.ExecuteBatch(batch) if err != nil { return nil, err } err = batch.Close() if err != nil { return nil, err } batch = kvWriter.NewBatch() } } if i > 0 { s.logf("llStore.update, ExecuteBatch,"+ " path: %s, total: %d, start", s.llConfig["path"], i) err = kvWriter.ExecuteBatch(batch) if err != nil { return nil, err } s.logf("llStore.update, ExecuteBatch,"+ " path: %s: total: %d, done", s.llConfig["path"], i) } } kvReader, err := s.kvStore.Reader() if err != nil { return nil, err } s.logf("llStore.update, new reader") return &llSnapshot{ llStore: s.addRef(), kvReader: kvReader, refs: 1, }, nil }
// Attempt partial rollback. Implementation sketch: walk through // previous mossStore snapshots until we reach to a point at or before // the wanted rollbackSeq. If found, revert to that prevous snapshot. func (t *BleveDest) partialRollbackLOCKED(partition string, rollbackSeq uint64) (bool, bool, error) { if t.bindex == nil { return false, false, nil } _, kvstore, err := t.bindex.Advanced() if err != nil { return false, false, err } llsh, ok := kvstore.(LowerLevelStoreHolder) if !ok { return false, false, fmt.Errorf("kvstore not a llsh, kvstore: %#v", kvstore) } lls := llsh.LowerLevelStore() if lls == nil { return false, false, fmt.Errorf("lls nil") } msah, ok := lls.(MossStoreActualHolder) if !ok { return false, false, fmt.Errorf("llsh not a msah, llsh: %#v", llsh) } store := msah.Actual() if store == nil { return false, false, nil // No moss store, so no partial rollback. } store.AddRef() defer store.Close() // TODO: Handle non-upsidedown bleve index types some day. seqMaxKey := upsidedown.NewInternalRow([]byte(partition), nil).Key() totSnapshotsExamined := 0 defer func() { log.Printf("pindex_bleve_rollback: path: %s, totSnapshotsExamined: %d", t.path, totSnapshotsExamined) }() var ss, ssPrev moss.Snapshot ss, err = store.Snapshot() for err == nil && ss != nil { totSnapshotsExamined++ var tryRevert bool tryRevert, err = snapshotAtOrBeforeSeq(t.path, ss, seqMaxKey, rollbackSeq) if err != nil { ss.Close() return false, false, err } if tryRevert { log.Printf("pindex_bleve_rollback: trying revert, path: %s", t.path) // Close the bleve index, but keep our ref-counts on the // underlying store and snapshot until after the revert. t.closeLOCKED() err = store.SnapshotRevert(ss) ss.Close() return true, err == nil, err } ssPrev, err = store.SnapshotPrevious(ss) ss.Close() ss = ssPrev } return false, false, err }