Skip to content

Commit

Permalink
fix: Concurrency control of Document using RWMutex (#213)
Browse files Browse the repository at this point in the history
Inclusion of a reader/writer mutual exclusion lock on Document and its set operation.
  • Loading branch information
orpheuslummis committed Mar 4, 2022
1 parent 8dd342f commit 2b3d0b0
Showing 1 changed file with 24 additions and 12 deletions.
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()
}

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
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()
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

0 comments on commit 2b3d0b0

Please sign in to comment.