-
Notifications
You must be signed in to change notification settings - Fork 51
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
refactor: Conceptually clarify schema vs query-language #924
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,26 +13,34 @@ package db | |
import ( | ||
"context" | ||
|
||
"github.com/graphql-go/graphql/language/ast" | ||
dsq "github.com/ipfs/go-datastore/query" | ||
|
||
"github.com/sourcenetwork/defradb/client" | ||
"github.com/sourcenetwork/defradb/core" | ||
"github.com/sourcenetwork/defradb/query/graphql/schema" | ||
) | ||
|
||
// LoadSchema takes the provided schema in SDL format, and applies it to the database, | ||
// and creates the necessary collections, query types, etc. | ||
func (db *db) AddSchema(ctx context.Context, schemaString string) error { | ||
schema, err := db.parser.AddSchema(ctx, schemaString) | ||
err := db.parser.AddSchema(ctx, schemaString) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, desc := range schema.Descriptions { | ||
collectionDescriptions, schemaDefinitions, err := createDescriptions(ctx, schemaString) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, desc := range collectionDescriptions { | ||
if _, err := db.CreateCollection(ctx, desc); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return db.saveSchema(ctx, schema) | ||
return db.saveSchema(ctx, schemaDefinitions) | ||
} | ||
|
||
func (db *db) loadSchema(ctx context.Context) error { | ||
|
@@ -50,17 +58,53 @@ func (db *db) loadSchema(ctx context.Context) error { | |
sdl += "\n" + string(buf) | ||
} | ||
|
||
_, err = db.parser.AddSchema(ctx, sdl) | ||
return err | ||
return db.parser.AddSchema(ctx, sdl) | ||
} | ||
|
||
func (db *db) saveSchema(ctx context.Context, schema *core.Schema) error { | ||
func (db *db) saveSchema(ctx context.Context, schemaDefinitions []core.SchemaDefinition) error { | ||
// save each type individually | ||
for _, def := range schema.Definitions { | ||
for _, def := range schemaDefinitions { | ||
key := core.NewSchemaKey(def.Name) | ||
if err := db.systemstore().Put(ctx, key.ToDS(), def.Body); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func createDescriptions( | ||
ctx context.Context, | ||
schemaString string, | ||
) ([]client.CollectionDescription, []core.SchemaDefinition, error) { | ||
// We should not be using this package at all here, and should not be doing most of what this package does | ||
// Rework: https://github.com/sourcenetwork/defradb/issues/923 | ||
schemaManager, err := schema.NewSchemaManager() | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
types, astdoc, err := schemaManager.Generator.FromSDL(ctx, schemaString) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion(non-blocking): Do you want to expose the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, I do not want to make the existing generator code more complex by adding a new entry point, for the sake of temporary code. Performance doesn't matter much here, so the code-cleanliness and 'keep it as before and dont worry' is much preferred. |
||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
colDesc, err := schemaManager.Generator.CreateDescriptions(types) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
definitions := make([]core.SchemaDefinition, len(astdoc.Definitions)) | ||
for i, astDefinition := range astdoc.Definitions { | ||
objDef, isObjDef := astDefinition.(*ast.ObjectDefinition) | ||
if !isObjDef { | ||
continue | ||
} | ||
|
||
definitions[i] = core.SchemaDefinition{ | ||
Name: objDef.Name.Value, | ||
Body: objDef.Loc.Source.Body[objDef.Loc.Start:objDef.Loc.End], | ||
} | ||
} | ||
|
||
return colDesc, definitions, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,6 @@ import ( | |
"github.com/sourcenetwork/defradb/query/graphql/schema" | ||
|
||
gql "github.com/graphql-go/graphql" | ||
"github.com/graphql-go/graphql/language/ast" | ||
gqlp "github.com/graphql-go/graphql/language/parser" | ||
"github.com/graphql-go/graphql/language/source" | ||
) | ||
|
@@ -96,32 +95,7 @@ func (p *parser) Parse(request string) (*request.Request, []error) { | |
return query, nil | ||
} | ||
|
||
func (p *parser) AddSchema(ctx context.Context, schema string) (*core.Schema, error) { | ||
types, astdoc, err := p.schemaManager.Generator.FromSDL(ctx, schema) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
colDesc, err := p.schemaManager.Generator.CreateDescriptions(types) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
definitions := make([]core.SchemaDefinition, len(astdoc.Definitions)) | ||
for i, astDefinition := range astdoc.Definitions { | ||
objDef, isObjDef := astDefinition.(*ast.ObjectDefinition) | ||
if !isObjDef { | ||
continue | ||
} | ||
|
||
definitions[i] = core.SchemaDefinition{ | ||
Name: objDef.Name.Value, | ||
Body: objDef.Loc.Source.Body[objDef.Loc.Start:objDef.Loc.End], | ||
} | ||
} | ||
|
||
return &core.Schema{ | ||
Definitions: definitions, | ||
Descriptions: colDesc, | ||
}, nil | ||
func (p *parser) AddSchema(ctx context.Context, schema string) error { | ||
_, _, err := p.schemaManager.Generator.FromSDL(ctx, schema) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Is this simply cashing the schema for the GQL package? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generating (the query language types - used for validation and introspection) and caching them - this is done everytime the database is initialized (e.g. on restart), as well as when a new schema is provided |
||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A new schema manager is required, as each instance caches the types and reusing the gql-query-lang one would cause collisions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: I look forward do see what you're going to do here :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be quite simple I think, and we'll be able to go straight from ast to collectionDescription without worrying about gql-types or some virtual fields (IIRC it has to skip over stuff like _group and aggregates)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we want to do a new schema manager for each
createDescriptions
exec. You should be able to refer to previous types when registering new types. That wouldn't be possible if we have a new manager on each exec.I don't necessarily see the benefit of going from AST to Description without a gql type intermediary, but thats a problem that can be kicked down the road.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I forgot about that, and we have no tests for that either (and so it is quite possibly already broken, and I remember it having been broken before at one point - we need to cover this). For a test gap like this I'd be extremely tempted to say it is broken already, and we need to sort it out in another ticket anyway due to the scope-creep/likelyhood-of-finding-many-more-bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started working on this, and what you are saying is currently syntactically impossible with our current system. A schema cannot reference an existing type, as that type also needs to be amended to reference the new type (in the schema definition):
See how both definitions contain a reference to each other - you cannot add author, and then add book (without update, which we dont support atm).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realized this after the fact, indeed very true.
I still would've preferred a single instance of the schema, I don't really see the value in creating one for each request. There's no way to reference the collection GQL types in a consistent way.
What benefit does this approach have in your mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absence of state, simplicity, ease of ripping out, minimized risk of state-polution, and the fact that persisting it provides no assertable benefits atm