// In benchmarks, the time taken per AddMutation before was // plateauing at 2.5 ms with sync per 10 log entries, and increasing // for sync per 100 log entries (to 3 ms per AddMutation), largely because // of how index generation was being done. // // With this change, the benchmarks perform as good as benchmarks for // commit.Logger, where the less frequently file sync happens, the faster // AddMutations run. // // PASS // BenchmarkAddMutations_SyncEveryLogEntry-6 100 24712455 ns/op // BenchmarkAddMutations_SyncEvery10LogEntry-6 500 2485961 ns/op // BenchmarkAddMutations_SyncEvery100LogEntry-6 10000 298352 ns/op // BenchmarkAddMutations_SyncEvery1000LogEntry-6 30000 63544 ns/op // ok github.com/dgraph-io/dgraph/posting 10.291s func (l *List) AddMutation(t x.DirectedEdge, op byte) error { l.wg.Wait() l.Lock() defer l.Unlock() if l.deleteMe { return E_TMP_ERROR } if t.Timestamp.UnixNano() < l.maxMutationTs { return fmt.Errorf("Mutation ts lower than committed ts.") } // Mutation arrives: // - Check if we had any(SET/DEL) before this, stored in the mutation list. // - If yes, then replace that mutation. Jump to a) // a) check if the entity exists in main posting list. // - If yes, store the mutation. // - If no, disregard this mutation. // All edges with a value set, have the same uid. In other words, // an (entity, attribute) can only have one interface{} value. if t.Value != nil { t.ValueId = math.MaxUint64 } if t.ValueId == 0 { return fmt.Errorf("ValueId cannot be zero.") } mbuf := newPosting(t, op) uo := flatbuffers.GetUOffsetT(mbuf) mpost := new(types.Posting) mpost.Init(mbuf, uo) glog.WithFields(logrus.Fields{ "uid": mpost.Uid(), "source": string(mpost.Source()), "ts": mpost.Ts(), }).Debug("Add mutation") l.mergeMutation(mpost) l.maxMutationTs = t.Timestamp.UnixNano() if len(l.mindex)+len(l.mlayer) > 0 { atomic.StoreInt64(&l.dirtyTs, time.Now().UnixNano()) if dirtymap != nil { dirtymap.Put(l.ghash, true) } } if l.clog == nil { return nil } return l.clog.AddLog(t.Timestamp.UnixNano(), l.hash, mbuf) }
func addPosting(b *flatbuffers.Builder, p types.Posting) flatbuffers.UOffsetT { so := b.CreateByteString(p.Source()) // Do this before posting start. var bo flatbuffers.UOffsetT if p.ValueLength() > 0 { bo = b.CreateByteVector(p.ValueBytes()) } types.PostingStart(b) types.PostingAddUid(b, p.Uid()) if bo > 0 { types.PostingAddValue(b, bo) } types.PostingAddSource(b, so) types.PostingAddTs(b, p.Ts()) types.PostingAddOp(b, p.Op()) return types.PostingEnd(b) }
func (l *List) init(key []byte, pstore *store.Store, clog *commit.Logger) { l.Lock() defer l.Unlock() defer l.wg.Done() if len(empty) == 0 { glog.Fatal("empty should have some bytes.") } l.key = key l.pstore = pstore l.clog = clog posting := l.getPostingList() l.maxMutationTs = posting.CommitTs() l.hash = farm.Fingerprint32(key) l.ghash = gotomic.IntKey(farm.Fingerprint64(key)) l.mlayer = make(map[int]types.Posting) if clog == nil { return } glog.Debug("Starting stream entries...") err := clog.StreamEntries(posting.CommitTs()+1, l.hash, func(hdr commit.Header, buffer []byte) { uo := flatbuffers.GetUOffsetT(buffer) m := new(types.Posting) m.Init(buffer, uo) if m.Ts() > l.maxMutationTs { l.maxMutationTs = m.Ts() } glog.WithFields(logrus.Fields{ "uid": m.Uid(), "source": string(m.Source()), "ts": m.Ts(), }).Debug("Got entry from log") l.mergeMutation(m) }) if err != nil { glog.WithError(err).Error("While streaming entries.") } glog.Debug("Done streaming entries.") }