Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

always write index data for VDR, never check on duplicate #2141

Merged
merged 4 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions vdr/didstore/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ func (e event) before(other event) bool {
return e.Ref.Compare(other.Ref) < 0
}

// equal returns true when event.Ref are equal.
func (e event) equal(other event) bool {
return e.Ref.Equals(other.Ref)
}

// eventList is an in-memory representation of an Events shelf entry
type eventList struct {
Events []event `json:"events"`
Expand Down Expand Up @@ -92,3 +97,12 @@ func (el *eventList) insert(newEvent event) int {
el.Events = newList
return index
}

func (el *eventList) contains(newEvent event) bool {
for _, e := range el.Events {
if e.equal(newEvent) {
return true
}
}
return false
}
15 changes: 15 additions & 0 deletions vdr/didstore/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,18 @@ func TestEventList_insert(t *testing.T) {
assert.Equal(t, sha2s, el.Events[2].Ref)
})
}

func TestEventList_contains(t *testing.T) {
t.Run("false", func(t *testing.T) {
el := eventList{}

assert.False(t, el.contains(event{Ref: sha0s}))
})

t.Run("true", func(t *testing.T) {
el := eventList{}
el.insert(event{Ref: sha0s})

assert.True(t, el.contains(event{Ref: sha0s}))
})
}
9 changes: 0 additions & 9 deletions vdr/didstore/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,3 @@ func readEventList(tx stoabs.ReadTx, id did.DID) (eventList, error) {
}
return el, nil
}

func transactionExists(tx stoabs.ReadTx, ref hash.SHA256Hash) (bool, error) {
txReader := tx.GetShelfReader(transactionIndexShelf)
bytes, err := txReader.Get(stoabs.HashKey(ref))
if err != nil && !errors.Is(err, stoabs.ErrKeyNotFound) {
return false, err
}
return len(bytes) > 0, nil
}
35 changes: 0 additions & 35 deletions vdr/didstore/reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,38 +96,3 @@ func Test_readEventList(t *testing.T) {
require.NoError(t, err)
})
}

func Test_transactionExists(t *testing.T) {
store := NewTestStore(t)

t.Run("false", func(t *testing.T) {
err := store.db.Read(context.Background(), func(tx stoabs.ReadTx) error {
dup, _ := transactionExists(tx, hash.RandomHash())

assert.False(t, dup)

return nil
})
require.NoError(t, err)
})

t.Run("true", func(t *testing.T) {
transaction := newTestTransaction(did.Document{})

err := store.db.Write(context.Background(), func(tx stoabs.WriteTx) error {
txShelf := tx.GetShelfWriter(transactionIndexShelf)
_ = txShelf.Put(stoabs.HashKey(transaction.Ref), []byte{0})
return nil
})
require.NoError(t, err)

err = store.db.Read(context.Background(), func(tx stoabs.ReadTx) error {
dup, _ := transactionExists(tx, transaction.Ref)

assert.True(t, dup)

return nil
})
require.NoError(t, err)
})
}
29 changes: 18 additions & 11 deletions vdr/didstore/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,33 +75,40 @@ func (tl *store) Configure(_ core.ServerConfig) (err error) {
// Add inserts the document version at the correct place and updates all later versions if needed
// The integrity of the document has already been checked by the DAG.
func (tl *store) Add(didDocument did.Document, transaction Transaction) error {
// prevents parallel execution
// First write the document and transaction to the transactionIndexShelf and documentShelf.
// This operation is duplicate save, since it uses hash values as key.
// This operation must succeed because otherwise the second transaction will be broken forever.
// Due to the way Redis works, there's no guarantee all the data is written transactionally when
// executed in a single write operation.
err := tl.db.Write(context.Background(), func(tx stoabs.WriteTx) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this split in 2 write operations?

if exists, err := transactionExists(tx, transaction.Ref); err != nil {
return err
} else if exists {
return nil
// write document to documentShelf
err := writeDocument(tx, didDocument, transaction)
if err != nil {
return fmt.Errorf("writeDocument failed: %w", err)
}
return nil
}, stoabs.WithWriteLock())
if err != nil {
return fmt.Errorf("database error on commit: %w", err)
}

err = tl.db.Write(context.Background(), func(tx stoabs.WriteTx) error {
currentEventList, err := readEventList(tx, didDocument.ID)
if err != nil {
return fmt.Errorf("read eventList failed: %w", err)
}

// write document to documentShelf
err = writeDocument(tx, didDocument, transaction)
if err != nil {
return fmt.Errorf("writeDocument failed: %w", err)
transaction.document = &didDocument
if currentEventList.contains(event(transaction)) {
return nil
}

transaction.document = &didDocument
index := currentEventList.insert(event(transaction))
var base *event
applyList := currentEventList.Events[index:]
if index > 0 {
base = &currentEventList.Events[index-1]
}

if err = applyFrom(tx, base, applyList); err != nil {
return fmt.Errorf("applying event list failed: %w", err)
}
Expand Down
31 changes: 31 additions & 0 deletions vdr/didstore/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,37 @@ func TestStore_Add(t *testing.T) {
})
})

t.Run("duplicate ok", func(t *testing.T) {
store := NewTestStore(t)

require.NoError(t, store.Add(create, txCreate))
require.NoError(t, store.Add(create, txCreate))

err := store.db.ReadShelf(context.Background(), metadataShelf, func(reader stoabs.Reader) error {
metaBytes, err := reader.Get(stoabs.BytesKey(fmt.Sprintf("%s0", testDID.String())))
if err != nil {
return err
}
metadata := documentMetadata{}
err = json.Unmarshal(metaBytes, &metadata)
if err != nil {
return err
}

assert.Equal(t, txCreate.SigningTime.Unix(), metadata.Created.Unix())
assert.Equal(t, txCreate.SigningTime.Unix(), metadata.Updated.Unix())
assert.Nil(t, metadata.PreviousHash)
assert.Equal(t, txCreate.PayloadHash, metadata.Hash)
assert.Nil(t, metadata.PreviousTransaction)
assert.Equal(t, []hash.SHA256Hash{txCreate.Ref}, metadata.SourceTransactions)
assert.Equal(t, 0, metadata.Version)
assert.Equal(t, false, metadata.Deactivated)

return nil
})
require.NoError(t, err)
})

t.Run("update ok", func(t *testing.T) {
store := NewTestStore(t)

Expand Down