Skip to content

Commit

Permalink
Only load Thema's runtime once
Browse files Browse the repository at this point in the history
  • Loading branch information
sam boyer committed Apr 24, 2023
1 parent 6c033c8 commit 9b3f3c2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 37 deletions.
1 change: 0 additions & 1 deletion lineage.cue
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ import (
// TODO switch to descending order - newest on top is nicer to read
lenses: [...#Lens]

// TODO this is horrible. Remove ASAP.
_atLeastOneSchema: len(schemas) > 0

_schemas: [...]
Expand Down
81 changes: 47 additions & 34 deletions runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,58 @@ import (
"sync"

"cuelang.org/go/cue"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/load"
"github.com/grafana/thema/internal/util"
)

// Runtime is a gateway to the set of CUE constructs available in the Thema CUE
// package, allowing Go code to rely on the same functionality.
var rtOnce sync.Once
var themaBI *build.Instance

func loadRuntime() *build.Instance {
rtOnce.Do(func() {
path := filepath.Join(util.Prefix, "github.com", "grafana", "thema")

overlay := make(map[string]load.Source)
if err := util.ToOverlay(path, CueJointFS, overlay); err != nil {
// It's impossible for this to fail barring temporary bugs in filesystem
// layout within the thema rt itself. These should be trivially
// catchable during CI, so avoid forcing meaningless error handling on
// dependers and prefer a panic.
panic(err)
}

cfg := &load.Config{
Overlay: overlay,
Package: "thema",
Module: "github.com/grafana/thema",
Dir: path,
}
themaBI = load.Instances(nil, cfg)[0]

// proactively check, so we don't have to do it when making a new library
rt := cuecontext.New().BuildInstance(themaBI)
if err := rt.Validate(cue.All()); err != nil {
// As with the above, an error means that a problem exists in the
// literal CUE code embedded in this version of package (that should
// have trivially been caught with CI), so the caller can't fix anything
// without changing the version of the thema Go library they're
// depending on. It's a hard failure that should be unreachable outside
// thema internal testing, so just panic.
panic(errors.Details(err, nil))
}
})

return themaBI
}

// Runtime holds the set of CUE constructs available in the Thema CUE package,
// allowing Thema's Go code to internally reuse the same native CUE functionality.
//
// Each Thema Runtime is bound to a single cue.Context, set at the time
// of Runtime creation via NewRuntime.
// Each Thema Runtime is bound to a single cue.Context, determined by the parameter
// passed to [NewRuntime].
type Runtime struct {
// Value corresponds to loading the whole github.com/grafana/thema:thema
// package.
Expand All @@ -37,38 +79,9 @@ func NewRuntime(ctx *cue.Context) *Runtime {
if ctx == nil {
panic("nil context provided")
}

path := filepath.Join(util.Prefix, "github.com", "grafana", "thema")

overlay := make(map[string]load.Source)
if err := util.ToOverlay(path, CueJointFS, overlay); err != nil {
// It's impossible for this to fail barring temporary bugs in filesystem
// layout within the thema rt itself. These should be trivially
// catchable during CI, so avoid forcing meaningless error handling on
// dependers and prefer a panic.
panic(err)
}

cfg := &load.Config{
Overlay: overlay,
Package: "thema",
Module: "github.com/grafana/thema",
Dir: path,
}

rt := ctx.BuildInstance(load.Instances(nil, cfg)[0])
if err := rt.Validate(cue.All()); err != nil {
// As with the above, an error means that a problem exists in the
// literal CUE code embedded in this version of package (that should
// have trivially been caught with CI), so the caller can't fix anything
// without changing the version of the thema Go library they're
// depending on. It's a hard failure that should be unreachable outside
// thema internal testing, so just panic.
panic(errors.Details(err, nil))
}
rt := ctx.BuildInstance(loadRuntime())

// FIXME preload all the known funcs into a map[string]cue.Value here to avoid runtime cost

return &Runtime{
val: rt,
}
Expand Down
2 changes: 1 addition & 1 deletion testdata/invalidlineage/empty-schemas.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ thema.#Lineage
name: "empty-schemas"
schemas: []
-- out/bindfail --
#Lineage.schemas: incompatible list lengths (0 and 1) (and 32 more errors)
schemas: incompatible list lengths (0 and 1) (and 12 more errors)
2 changes: 1 addition & 1 deletion testdata/invalidlineage/second-schema-versionless.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ lenses: [
}
]
-- out/bindfail --
#Lineage._schemasAreOrdered.1: conflicting values true and false (and 577 more errors)
_schemasAreOrdered.1: conflicting values true and false (and 80 more errors)

0 comments on commit 9b3f3c2

Please sign in to comment.