From 8aacc8bfdcf72bd607bb491ce2533c5c0ef2313e Mon Sep 17 00:00:00 2001 From: Quentin Machu Date: Tue, 17 Nov 2015 15:31:29 -0500 Subject: [PATCH] database: Ensure that quads in a tx are applied in the desired order. --- Godeps/Godeps.json | 2 +- .../google/cayley/graph/transaction.go | 61 +++++++++++++------ .../github.com/google/cayley/writer/single.go | 10 ++- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index d3b4b195..91a9b4b4 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -42,7 +42,7 @@ { "ImportPath": "github.com/google/cayley", "Comment": "v0.4.1-160-gcdf0154", - "Rev": "6027e4c48b3385307bd4bea462149fd544577165" + "Rev": "cfbc0b364910ab69bbced5d2f7913ee7e80a0c80" }, { "ImportPath": "github.com/julienschmidt/httprouter", diff --git a/vendor/github.com/google/cayley/graph/transaction.go b/vendor/github.com/google/cayley/graph/transaction.go index a37d9df4..39597109 100644 --- a/vendor/github.com/google/cayley/graph/transaction.go +++ b/vendor/github.com/google/cayley/graph/transaction.go @@ -18,32 +18,28 @@ import "github.com/google/cayley/quad" // Transaction stores a bunch of Deltas to apply atomatically on the database. type Transaction struct { - Deltas map[Delta]struct{} + // Deltas stores the deltas in the right order + Deltas []Delta + // deltas stores the deltas in a map to avoid duplications + deltas map[Delta]struct{} } // NewTransaction initialize a new transaction. func NewTransaction() *Transaction { - return &Transaction{Deltas: make(map[Delta]struct{}, 100)} + return &Transaction{Deltas: make([]Delta, 0, 10), deltas: make(map[Delta]struct{}, 10)} } // AddQuad adds a new quad to the transaction if it is not already present in it. // If there is a 'remove' delta for that quad, it will remove that delta from // the transaction instead of actually addind the quad. func (t *Transaction) AddQuad(q quad.Quad) { - ad := Delta{ - Quad: q, - Action: Add, - } - rd := Delta{ - Quad: q, - Action: Delete, - } + ad, rd := createDeltas(q) - if _, adExists := t.Deltas[ad]; !adExists { - if _, rdExists := t.Deltas[rd]; rdExists { - delete(t.Deltas, rd) + if _, adExists := t.deltas[ad]; !adExists { + if _, rdExists := t.deltas[rd]; rdExists { + t.deleteDelta(rd) } else { - t.Deltas[ad] = struct{}{} + t.addDelta(ad) } } } @@ -52,14 +48,41 @@ func (t *Transaction) AddQuad(q quad.Quad) { // The quad will be removed from the database if it is not present in the // transaction, otherwise it simply remove it from the transaction. func (t *Transaction) RemoveQuad(q quad.Quad) { - ad := Delta{ + ad, rd := createDeltas(q) + + if _, adExists := t.deltas[ad]; adExists { + t.deleteDelta(ad) + } else { + if _, rdExists := t.deltas[rd]; !rdExists { + t.addDelta(rd) + } + } +} + +func createDeltas(q quad.Quad) (ad, rd Delta) { + ad = Delta{ Quad: q, Action: Add, } + rd = Delta{ + Quad: q, + Action: Delete, + } + return +} - if _, adExists := t.Deltas[ad]; adExists { - delete(t.Deltas, ad) - } else { - t.Deltas[Delta{Quad: q, Action: Delete}] = struct{}{} +func (t *Transaction) addDelta(d Delta) { + t.Deltas = append(t.Deltas, d) + t.deltas[d] = struct{}{} +} + +func (t *Transaction) deleteDelta(d Delta) { + delete(t.deltas, d) + + for i, id := range t.Deltas { + if id == d { + t.Deltas = append(t.Deltas[:i], t.Deltas[i+1:]...) + break + } } } diff --git a/vendor/github.com/google/cayley/writer/single.go b/vendor/github.com/google/cayley/writer/single.go index 376f9e19..b1b955d0 100644 --- a/vendor/github.com/google/cayley/writer/single.go +++ b/vendor/github.com/google/cayley/writer/single.go @@ -109,11 +109,9 @@ func (s *Single) Close() error { func (s *Single) ApplyTransaction(t *graph.Transaction) error { ts := time.Now() - deltas := make([]graph.Delta, 0, len(t.Deltas)) - for d := range t.Deltas { - d.ID = s.currentID.Next() - d.Timestamp = ts - deltas = append(deltas, d) + for i := 0; i < len(t.Deltas); i++ { + t.Deltas[i].ID = s.currentID.Next() + t.Deltas[i].Timestamp = ts } - return s.qs.ApplyDeltas(deltas, s.ignoreOpts) + return s.qs.ApplyDeltas(t.Deltas, s.ignoreOpts) }