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

fix: Concurrency control of Document using RWMutex #213

Merged
merged 6 commits into from
Mar 4, 2022
Merged
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
36 changes: 24 additions & 12 deletions document/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"errors"
"fmt"
"strings"
"sync"

"github.com/fxamacker/cbor/v2"
"github.com/ipfs/go-cid"
Expand Down Expand Up @@ -68,18 +69,12 @@ type Document struct {
fields map[string]Field
values map[Field]Value
// @TODO: schemaInfo schema.Info

head cid.Cid

mu sync.RWMutex
// marks if document has unsaved changes
isDirty bool
}

// New returns a newly instanciated Document
func New() *Document {
return newEmptyDoc()
}

orpheuslummis marked this conversation as resolved.
Show resolved Hide resolved
func NewWithKey(key key.DocKey) *Document {
doc := newEmptyDoc()
doc.key = key
Expand Down Expand Up @@ -159,15 +154,20 @@ func NewFromJSON(obj []byte, schema ...base.SchemaDescription) (*Document, error
}

func (doc *Document) Head() cid.Cid {
doc.mu.RLock()
defer doc.mu.RUnlock()
return doc.head
}

func (doc *Document) SetHead(head cid.Cid) {
doc.mu.Lock()
defer doc.mu.Unlock()
doc.head = head
}

// Key returns the generated DocKey for this document
func (doc *Document) Key() key.DocKey {
// Reading without a read-lock as we assume the DocKey is immutable
orpheuslummis marked this conversation as resolved.
Show resolved Hide resolved
return doc.key
}

Expand All @@ -188,6 +188,8 @@ func (doc *Document) Get(field string) (interface{}, error) {

// GetValue given a field as a string, return the Value type
func (doc *Document) GetValue(field string) (Value, error) {
doc.mu.RLock()
defer doc.mu.RUnlock()
path, subPaths, hasSubPaths := parseFieldPath(field)
f, exists := doc.fields[path]
if !exists {
Expand All @@ -210,6 +212,8 @@ func (doc *Document) GetValue(field string) (Value, error) {

// GetValueWithField gets the Value type from a given Field type
func (doc *Document) GetValueWithField(f Field) (Value, error) {
doc.mu.RLock()
defer doc.mu.RUnlock()
v, exists := doc.values[f]
if !exists {
return nil, ErrFieldNotExist
Expand Down Expand Up @@ -253,14 +257,14 @@ func (doc *Document) SetAs(field string, value interface{}, t core.CType) error

// Delete removes a field, and marks it to be deleted on the following db.Update() call
func (doc *Document) Delete(fields ...string) error {
doc.mu.Lock()
defer doc.mu.Unlock()
for _, f := range fields {
field, exists := doc.fields[f]
if !exists {
return ErrFieldNotExist
}

val := doc.values[field]
val.Delete()
doc.values[field].Delete()
}
return nil
}
Expand All @@ -270,9 +274,9 @@ func (doc *Document) Delete(fields ...string) error {
// return doc.set(t, field, value)
// }

// set implementation
// @todo Apply locking on Document field/value operations
func (doc *Document) set(t core.CType, field string, value Value) error {
doc.mu.Lock()
orpheuslummis marked this conversation as resolved.
Show resolved Hide resolved
defer doc.mu.Unlock()
var f Field
if v, exists := doc.fields[field]; exists {
f = v
Expand Down Expand Up @@ -383,11 +387,15 @@ func (doc *Document) setAndParseObjectType(value map[string]interface{}) error {

// Fields gets the document fields as a map
func (doc *Document) Fields() map[string]Field {
doc.mu.RLock()
defer doc.mu.RUnlock()
return doc.fields
}

// Values gets the document values as a map
func (doc *Document) Values() map[Field]Value {
doc.mu.RLock()
defer doc.mu.RUnlock()
return doc.values
}

Expand Down Expand Up @@ -447,6 +455,8 @@ func (doc *Document) Clean() {
// converts the document into a map[string]interface{}
// including any sub documents
func (doc *Document) toMap() (map[string]interface{}, error) {
doc.mu.RLock()
defer doc.mu.RUnlock()
docMap := make(map[string]interface{})
for k, v := range doc.fields {
value, exists := doc.values[v]
Expand All @@ -471,6 +481,8 @@ func (doc *Document) toMap() (map[string]interface{}, error) {
}

func (doc *Document) toMapWithKey() (map[string]interface{}, error) {
doc.mu.RLock()
defer doc.mu.RUnlock()
docMap := make(map[string]interface{})
for k, v := range doc.fields {
value, exists := doc.values[v]
Expand Down