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

Enable concurrenct access to SchemaLoader #323

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion schemaLoader.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func NewSchemaLoader() *SchemaLoader {

ps := &SchemaLoader{
pool: &schemaPool{
schemaPoolDocuments: make(map[string]*schemaPoolDocument),
schemaPoolDocuments: new(schemaPoolDocuments),
},
AutoDetect: true,
Validate: false,
Expand Down
16 changes: 8 additions & 8 deletions schemaPool.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type schemaPoolDocument struct {
}

type schemaPool struct {
schemaPoolDocuments map[string]*schemaPoolDocument
schemaPoolDocuments *schemaPoolDocuments
jsonLoaderFactory JSONLoaderFactory
autoDetect *bool
}
Expand All @@ -53,7 +53,7 @@ func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.J
reference = ref.String()
)
// Only the root document should be added to the schema pool if pooled is true
if _, ok := p.schemaPoolDocuments[reference]; pooled && ok {
if _, ok := p.schemaPoolDocuments.Load(reference); pooled && ok {
return fmt.Errorf("Reference already exists: \"%s\"", reference)
}

Expand All @@ -67,7 +67,7 @@ func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.J
err = p.parseReferencesRecursive(document, ref, draft)

if pooled {
p.schemaPoolDocuments[reference] = &schemaPoolDocument{Document: document, Draft: draft}
p.schemaPoolDocuments.Store(reference, &schemaPoolDocument{Document: document, Draft: draft})
}

return err
Expand Down Expand Up @@ -97,10 +97,10 @@ func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonre
if err == nil {
localRef, err = ref.Inherits(jsonReference)
if err == nil {
if _, ok := p.schemaPoolDocuments[localRef.String()]; ok {
if _, ok := p.schemaPoolDocuments.Load(localRef.String()); ok {
return fmt.Errorf("Reference already exists: \"%s\"", localRef.String())
}
p.schemaPoolDocuments[localRef.String()] = &schemaPoolDocument{Document: document, Draft: draft}
p.schemaPoolDocuments.Store(localRef.String(), &schemaPoolDocument{Document: document, Draft: draft})
}
}
}
Expand Down Expand Up @@ -155,7 +155,7 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche
// First check if the given fragment is a location independent identifier
// http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3

if spd, ok = p.schemaPoolDocuments[refToURL.String()]; ok {
if spd, ok = p.schemaPoolDocuments.Load(refToURL.String()); ok {
if internalLogEnabled {
internalLog(" From pool")
}
Expand All @@ -167,7 +167,7 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche

refToURL.GetUrl().Fragment = ""

if cachedSpd, ok := p.schemaPoolDocuments[refToURL.String()]; ok {
if cachedSpd, ok := p.schemaPoolDocuments.Load(refToURL.String()); ok {
document, _, err := reference.GetPointer().Get(cachedSpd.Document)

if err != nil {
Expand All @@ -179,7 +179,7 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche
}

spd = &schemaPoolDocument{Document: document, Draft: cachedSpd.Draft}
p.schemaPoolDocuments[reference.String()] = spd
p.schemaPoolDocuments.Store(reference.String(), spd)

return spd, nil
}
Expand Down
48 changes: 48 additions & 0 deletions schemaPoolDocuments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package gojsonschema

import "sync"

type schemaPoolDocuments struct {
mp sync.Map
}

func (spd *schemaPoolDocuments) Load(key string) (*schemaPoolDocument, bool) {
val, ok := spd.mp.Load(key)
Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

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

tbh, I wonder if the added complexity of using a sync.Map is needed for this use-case. sync.Map (by itself) does not have type-safety, so requires all the extra boilerplating in this file, and while sync.Map can perform better, in many situations it may actually be slower (unless high concurrency, large number of cores); https://medium.com/@deckarep/the-new-kid-in-town-gos-sync-map-de24a6bf7c2c

if !ok {
return nil, false
}
return val.(*schemaPoolDocument), true
}

func (spd *schemaPoolDocuments) Delete(key string) {
spd.mp.Delete(key)

}

func (spd *schemaPoolDocuments) LoadAndDelete(key string) (*schemaPoolDocument, bool) {
val, ok := spd.mp.LoadAndDelete(key)
if !ok {
return nil, false
}
return val.(*schemaPoolDocument), true
}

func (spd *schemaPoolDocuments) LoadOrStore(key string, value *schemaPoolDocument) (*schemaPoolDocument, bool) {
actual, ok := spd.mp.LoadOrStore(key, value)
if !ok {
return nil, false
}
return actual.(*schemaPoolDocument), true
}

func (spd *schemaPoolDocuments) Range(f func(key string, value *schemaPoolDocument) bool) {
spd.mp.Range(func(key, value interface{}) bool {
typedKey := key.(string)
typedVal := value.(*schemaPoolDocument)
return f(typedKey, typedVal)
})
}

func (spd *schemaPoolDocuments) Store(key string, value *schemaPoolDocument) {
spd.mp.Store(key, value)
}