Skip to content

Commit 6af5e79

Browse files
storage: Optimized read mode for default data storage
A new optimized read mode has been added to the default in-memory store, where data written to the store is eagerly converted to AST values (the data format used during evaluation). This pre-converted data is faster to read, and won’t cause memory spikes during load; but comes with slower data writes (affects startup and bundle load/update time) and a larger lowest overall memory footprint for OPA. Can be enabled for `opa run`, `opa eval`, and `opa bench` by setting the `—optimize-store-for-read-speed`. See http://localhost:8888/docs/edge/policy-performance/#storage-optimization. Implements: #4147 Signed-off-by: Johan Fylling <[email protected]> Co-authored-by: Ashutosh Narkar <[email protected]>
1 parent 1b797d9 commit 6af5e79

File tree

24 files changed

+3013
-1079
lines changed

24 files changed

+3013
-1079
lines changed

ast/term.go

+21
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,11 @@ func (arr *Array) Elem(i int) *Term {
12931293
return arr.elems[i]
12941294
}
12951295

1296+
// Set sets the element i of arr.
1297+
func (arr *Array) Set(i int, v *Term) {
1298+
arr.set(i, v)
1299+
}
1300+
12961301
// rehash updates the cached hash of arr.
12971302
func (arr *Array) rehash() {
12981303
arr.hash = 0
@@ -1306,6 +1311,7 @@ func (arr *Array) set(i int, v *Term) {
13061311
arr.ground = arr.ground && v.IsGround()
13071312
arr.elems[i] = v
13081313
arr.hashs[i] = v.Value.Hash()
1314+
arr.rehash()
13091315
}
13101316

13111317
// Slice returns a slice of arr starting from i index to j. -1
@@ -2560,6 +2566,8 @@ func (obj *object) insert(k, v *Term) {
25602566
}
25612567

25622568
curr.value = v
2569+
2570+
obj.rehash()
25632571
return
25642572
}
25652573
}
@@ -2584,6 +2592,19 @@ func (obj *object) insert(k, v *Term) {
25842592
}
25852593
}
25862594

2595+
func (obj *object) rehash() {
2596+
// obj.keys is considered truth, from which obj.hash and obj.elems are recalculated.
2597+
2598+
obj.hash = 0
2599+
obj.elems = make(map[int]*objectElem, len(obj.keys))
2600+
2601+
for _, elem := range obj.keys {
2602+
hash := elem.key.Hash()
2603+
obj.hash += hash + elem.value.Hash()
2604+
obj.elems[hash] = elem
2605+
}
2606+
}
2607+
25872608
func filterObject(o Value, filter Value) (Value, error) {
25882609
if filter.Compare(Null{}) == 0 {
25892610
return o, nil

bundle/store.go

+48-15
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,25 @@ func metadataPath(name string) storage.Path {
5959
return append(BundlesBasePath, name, "manifest", "metadata")
6060
}
6161

62+
func read(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (interface{}, error) {
63+
value, err := store.Read(ctx, txn, path)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
if astValue, ok := value.(ast.Value); ok {
69+
value, err = ast.JSON(astValue)
70+
if err != nil {
71+
return nil, err
72+
}
73+
}
74+
75+
return value, nil
76+
}
77+
6278
// ReadBundleNamesFromStore will return a list of bundle names which have had their metadata stored.
6379
func ReadBundleNamesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) ([]string, error) {
64-
value, err := store.Read(ctx, txn, BundlesBasePath)
80+
value, err := read(ctx, store, txn, BundlesBasePath)
6581
if err != nil {
6682
return nil, err
6783
}
@@ -153,7 +169,7 @@ func eraseWasmModulesFromStore(ctx context.Context, store storage.Store, txn sto
153169
// ReadWasmMetadataFromStore will read Wasm module resolver metadata from the store.
154170
func ReadWasmMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) ([]WasmResolver, error) {
155171
path := wasmEntrypointsPath(name)
156-
value, err := store.Read(ctx, txn, path)
172+
value, err := read(ctx, store, txn, path)
157173
if err != nil {
158174
return nil, err
159175
}
@@ -176,7 +192,7 @@ func ReadWasmMetadataFromStore(ctx context.Context, store storage.Store, txn sto
176192
// ReadWasmModulesFromStore will write Wasm module resolver metadata from the store.
177193
func ReadWasmModulesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (map[string][]byte, error) {
178194
path := wasmModulePath(name)
179-
value, err := store.Read(ctx, txn, path)
195+
value, err := read(ctx, store, txn, path)
180196
if err != nil {
181197
return nil, err
182198
}
@@ -205,7 +221,7 @@ func ReadWasmModulesFromStore(ctx context.Context, store storage.Store, txn stor
205221
// If the bundle is not activated, this function will return
206222
// storage NotFound error.
207223
func ReadBundleRootsFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) ([]string, error) {
208-
value, err := store.Read(ctx, txn, rootsPath(name))
224+
value, err := read(ctx, store, txn, rootsPath(name))
209225
if err != nil {
210226
return nil, err
211227
}
@@ -235,7 +251,7 @@ func ReadBundleRevisionFromStore(ctx context.Context, store storage.Store, txn s
235251
}
236252

237253
func readRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (string, error) {
238-
value, err := store.Read(ctx, txn, path)
254+
value, err := read(ctx, store, txn, path)
239255
if err != nil {
240256
return "", err
241257
}
@@ -256,7 +272,7 @@ func ReadBundleMetadataFromStore(ctx context.Context, store storage.Store, txn s
256272
}
257273

258274
func readMetadataFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (map[string]interface{}, error) {
259-
value, err := store.Read(ctx, txn, path)
275+
value, err := read(ctx, store, txn, path)
260276
if err != nil {
261277
return nil, suppressNotFound(err)
262278
}
@@ -277,7 +293,7 @@ func ReadBundleEtagFromStore(ctx context.Context, store storage.Store, txn stora
277293
}
278294

279295
func readEtagFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (string, error) {
280-
value, err := store.Read(ctx, txn, path)
296+
value, err := read(ctx, store, txn, path)
281297
if err != nil {
282298
return "", err
283299
}
@@ -544,14 +560,7 @@ func activateDeltaBundles(opts *ActivateOpts, bundles map[string]*Bundle) error
544560
return err
545561
}
546562

547-
bs, err := json.Marshal(value)
548-
if err != nil {
549-
return fmt.Errorf("corrupt manifest data: %w", err)
550-
}
551-
552-
var manifest Manifest
553-
554-
err = util.UnmarshalJSON(bs, &manifest)
563+
manifest, err := valueToManifest(value)
555564
if err != nil {
556565
return fmt.Errorf("corrupt manifest data: %w", err)
557566
}
@@ -585,6 +594,30 @@ func activateDeltaBundles(opts *ActivateOpts, bundles map[string]*Bundle) error
585594
return nil
586595
}
587596

597+
func valueToManifest(v interface{}) (Manifest, error) {
598+
if astV, ok := v.(ast.Value); ok {
599+
var err error
600+
v, err = ast.JSON(astV)
601+
if err != nil {
602+
return Manifest{}, err
603+
}
604+
}
605+
606+
var manifest Manifest
607+
608+
bs, err := json.Marshal(v)
609+
if err != nil {
610+
return Manifest{}, err
611+
}
612+
613+
err = util.UnmarshalJSON(bs, &manifest)
614+
if err != nil {
615+
return Manifest{}, err
616+
}
617+
618+
return manifest, nil
619+
}
620+
588621
// erase bundles by name and roots. This will clear all policies and data at its roots and remove its
589622
// manifest from storage.
590623
func eraseBundles(ctx context.Context, store storage.Store, txn storage.Transaction, parserOpts ast.ParserOptions, names map[string]struct{}, roots map[string]struct{}) (map[string]*ast.Module, error) {

0 commit comments

Comments
 (0)