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

Implementation is not thread safe #80

Open
Arqu opened this issue Sep 10, 2020 · 10 comments
Open

Implementation is not thread safe #80

Arqu opened this issue Sep 10, 2020 · 10 comments
Assignees
Labels
bug Something isn't working

Comments

@Arqu
Copy link
Contributor

Arqu commented Sep 10, 2020

The current implementation is not thread safe (mostly due to the global schema registry) and should be improved.

Known reports: #76 (comment)

@Arqu Arqu added the bug Something isn't working label Sep 10, 2020
@Arqu Arqu self-assigned this Sep 10, 2020
carolynvs added a commit to carolynvs/cnab-go that referenced this issue Nov 24, 2020
jsonschema isn't thread-safe,
qri-io/jsonschema#80,
so this adds a lock around the part that accesses a package level
variable so that we can use it in goroutines.

Signed-off-by: Carolyn Van Slyck <me@carolynvanslyck.com>
@carolynvs
Copy link
Contributor

Do you know what type of fix you would accept as a PR? I had to workaround this, and ended up making the keyword registry a struct so that after it's constructed, you aren't modifying a package variable anymore. The solution does use locks though in order to preserve the original API.

I can submit a PR if that solution seems okay to you.

@bmfs
Copy link
Contributor

bmfs commented May 7, 2021

The schema registry might suffer from the same problem.

Would it be viable to make both schemaRegistry and keywordRegistry scoped to the jsonschema.Schema instance?

Was looking into @carolynvs fix, and seems to be a step in the right direction, although the keywordRegistry still is globally scoped.

@carolynvs
Copy link
Contributor

Yeah sorry my fix wasn't everything that is necessary to address the problem. It was just enough to fix the code paths that we use for our project.

@bmfs
Copy link
Contributor

bmfs commented May 24, 2021

@Arqu Is @carolynvs implementation what you were thinking as a solution to this ticket?

I'm in need of a solution to this, and willing to work on it.
My plan is to continue @carolynvs work and extend it to include the schema registry ( and to the loader registry I've added in the #101 ).

Any concerns or suggestions that help on such PR being accepted are more than welcome.

@Arqu
Copy link
Contributor Author

Arqu commented May 24, 2021

I'm not too sure of all the details we will have to deal with on this front, however the shortest path there that I see would be to wrap the initialization of a schema (rs := &jsonschema.Schema{} from the readme) into a higher level instance and initialize with a function call for a default initialization and a call for user supplied registries.
In either case the parent, let's call it SchemaInstance, would be the holder of those registries and all calls bellow would source their structs from there. This will probably require a bit of refactoring work to get there fully, but am happy to assist on it and see it through.

@dataoleg
Copy link

Thank you for looking into this!

I would like to highlight that some other data structures in the jsonschema package are also not thread safe: we have recently encountered an issue with jsonschema.RegisterValidator method (for context: we use cnab-go stack and this method is called when unmarshaling cnab-go/bundle/definition.Schema, and it breaks during concurrent bundle loading).
A small example exposing concurrent map writes:

import (
	"github.com/qri-io/jsonschema"
	"sync"
	"testing"
)

func BenchmarkRegisterValidator(b *testing.B) {
	var wg sync.WaitGroup
	wg.Add(100)
	for i := 0; i < 100; i++ {
		go func() {
			jsonschema.RegisterValidator("name", jsonschema.NewType)
			wg.Done()
		}()
	}
	wg.Wait()
}

A higher-level cnab-go example triggering the same issue:

import (
	"github.com/cnabio/cnab-go/bundle/loader"
	"sync"
	"testing"
)

func BenchmarkLoadData(b *testing.B) {
	var wg sync.WaitGroup
	wg.Add(100)
	for i := 0; i < 100; i++ {
		go func() {
			loader.NewLoader().LoadData([]byte(`{"definitions":{"string":{"type":"string"}}}`))
			wg.Done()
		}()
	}
	wg.Wait()
}

@carolynvs
Copy link
Contributor

@dataoleg Porter is using a fork of this library with a fix for that. Here is the open PR to cnab-go and the fork.

cnab-go was reluctant to use my patch because it won't be accepted upstream in this repo. If there is another person needing the patch besides Porter, maybe that is enough support to get it merged into cnab-go until a better fix is available.

@carolynvs
Copy link
Contributor

I have submitted a PR with the patch we have been using in cnab and porter. #103 I'm not sure this is what you were looking for, but maybe it can get us thinking about a solution.

@carolynvs
Copy link
Contributor

#103 has been merged. It doesn't completely fix all thread-safety concerns but it does make calls to Validate and RegisterKeyword safe.

@sharenz
Copy link

sharenz commented Aug 12, 2021

Unfortunately Validate is not completly thread-safe yet. I found at least one more problem calling ValidateBytes.

==================
WARNING: DATA RACE
Read at 0x00c0005903d8 by goroutine 79:
  github.com/qri-io/jsonschema.(*Schema).Register()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:60 +0x5c
  github.com/qri-io/jsonschema.(*Schema).ValidateKeyword()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:280 +0xa8
  github.com/qri-io/jsonschema.(*Schema).Validate()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:260 +0x4ec
  github.com/qri-io/jsonschema.(*Schema).ValidateBytes()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:330 +0x17c
 
 
 
Previous write at 0x00c0005903d8 by goroutine 78:
  github.com/qri-io/jsonschema.(*Schema).Register()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:63 +0x74
  github.com/qri-io/jsonschema.(*Schema).ValidateKeyword()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:280 +0xa8
  github.com/qri-io/jsonschema.(*Schema).Validate()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:260 +0x4ec
  github.com/qri-io/jsonschema.(*Schema).ValidateBytes()
      /Users/xx/go/pkg/mod/github.com/qri-io/jsonschema@v0.2.1/schema.go:330 +0x17c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants