Skip to content

Commit

Permalink
Add cached views
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewSisley committed Sep 13, 2024
1 parent 49ddc49 commit 4b78c74
Show file tree
Hide file tree
Showing 55 changed files with 1,504 additions and 131 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/test-and-upload-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
lens-type: [wasm-time]
acp-type: [local]
database-encryption: [false]
view-type: [cacheless]
include:
- os: ubuntu-latest
client-type: go
Expand All @@ -45,48 +46,63 @@ jobs:
lens-type: wasm-time
acp-type: local
database-encryption: true
view-type: cacheless
- os: ubuntu-latest
client-type: go
database-type: badger-memory
mutation-type: collection-save
lens-type: wazero
acp-type: local
database-encryption: false
view-type: cacheless
- os: ubuntu-latest
client-type: go
database-type: badger-memory
mutation-type: collection-save
lens-type: wasmer
acp-type: local
database-encryption: false
view-type: cacheless
- os: ubuntu-latest
client-type: go
database-type: badger-memory
mutation-type: collection-save
lens-type: wasm-time
acp-type: source-hub
database-encryption: false
view-type: cacheless
- os: ubuntu-latest
client-type: http
database-type: badger-memory
mutation-type: collection-save
lens-type: wasm-time
acp-type: source-hub
database-encryption: false
view-type: cacheless
- os: ubuntu-latest
client-type: cli
database-type: badger-memory
mutation-type: collection-save
lens-type: wasm-time
acp-type: source-hub
database-encryption: false
view-type: cacheless
- os: ubuntu-latest
client-type: go
database-type: badger-memory
mutation-type: collection-save
lens-type: wasm-time
acp-type: local
database-encryption: false
view-type: materialized
- os: macos-latest
client-type: go
database-type: badger-memory
mutation-type: collection-save
lens-type: wasm-time
acp-type: local
database-encryption: false
view-type: cacheless
## TODO: https://github.com/sourcenetwork/defradb/issues/2080
## Uncomment the lines below to Re-enable the windows build once this todo is resolved.
## - os: windows-latest
Expand All @@ -96,6 +112,7 @@ jobs:
## lens-type: wasm-time
## acp-type: local
## database-encryption: false
## view-type: cacheless

runs-on: ${{ matrix.os }}

Expand All @@ -115,6 +132,7 @@ jobs:
DEFRA_MUTATION_TYPE: ${{ matrix.mutation-type }}
DEFRA_LENS_TYPE: ${{ matrix.lens-type }}
DEFRA_ACP_TYPE: ${{ matrix.acp-type }}
DEFRA_VIEW_TYPE: ${{ matrix.view-type }}

steps:
- name: Checkout code into the directory
Expand Down
1 change: 1 addition & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func NewDefraCommand() *cobra.Command {
view := MakeViewCommand()
view.AddCommand(
MakeViewAddCommand(),
MakeViewRefreshCommand(),
)

index := MakeIndexCommand()
Expand Down
74 changes: 74 additions & 0 deletions cli/view_refresh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2024 Democratized Data Foundation
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package cli

import (
"github.com/sourcenetwork/immutable"
"github.com/spf13/cobra"

"github.com/sourcenetwork/defradb/client"
)

func MakeViewRefreshCommand() *cobra.Command {
var name string
var schemaRoot string
var versionID string
var getInactive bool
var cmd = &cobra.Command{
Use: "refresh",
Short: "Refresh views.",
Long: `Refresh views, executing the underlying query and LensVm transforms and
persisting the results.
View is refreshed as the current user, meaning results returned for all subsequent query requests
to the view will recieve items accessible to the user refreshing the view's permissions.
Example: refresh all views
defradb view refresh
Example: refresh views by name
defradb view refresh --name UserView
Example: refresh views by schema root id
defradb view refresh --schema bae123
Example: refresh views by version id. This will also return inactive views
defradb view refresh --version bae123
`,
RunE: func(cmd *cobra.Command, args []string) error {
store := mustGetContextStore(cmd)

options := client.CollectionFetchOptions{}
if versionID != "" {
options.SchemaVersionID = immutable.Some(versionID)

Check warning on line 51 in cli/view_refresh.go

View check run for this annotation

Codecov / codecov/patch

cli/view_refresh.go#L51

Added line #L51 was not covered by tests
}
if schemaRoot != "" {
options.SchemaRoot = immutable.Some(schemaRoot)

Check warning on line 54 in cli/view_refresh.go

View check run for this annotation

Codecov / codecov/patch

cli/view_refresh.go#L54

Added line #L54 was not covered by tests
}
if name != "" {
options.Name = immutable.Some(name)

Check warning on line 57 in cli/view_refresh.go

View check run for this annotation

Codecov / codecov/patch

cli/view_refresh.go#L57

Added line #L57 was not covered by tests
}
if getInactive {
options.IncludeInactive = immutable.Some(getInactive)

Check warning on line 60 in cli/view_refresh.go

View check run for this annotation

Codecov / codecov/patch

cli/view_refresh.go#L60

Added line #L60 was not covered by tests
}

return store.RefreshViews(
cmd.Context(),
options,
)
},
}
cmd.Flags().StringVar(&name, "name", "", "View name")
cmd.Flags().StringVar(&schemaRoot, "schema", "", "View schema Root")
cmd.Flags().StringVar(&versionID, "version", "", "View version ID")
cmd.Flags().BoolVar(&getInactive, "get-inactive", false, "Get inactive views as well as active")
return cmd
}
11 changes: 11 additions & 0 deletions client/collection_description.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ type CollectionDescription struct {
// parsing is done, to avoid storing an invalid policyID or policy resource
// that may not even exist on acp.
Policy immutable.Option[PolicyDescription]

// IsMaterialized defines whether the items in this collection are cached or not.
//
// If it is true, they will be, if false, the data returned on query will be calculated
// at query-time from source.
//
// At the moment this can only be set to `false` if this collection sources it's data from
// another collection/query (is a View).
IsMaterialized bool
}

// QuerySource represents a collection data source from a query.
Expand Down Expand Up @@ -179,6 +188,7 @@ type collectionDescription struct {
ID uint32
RootID uint32
SchemaVersionID string
IsMaterialized bool
Policy immutable.Option[PolicyDescription]
Indexes []IndexDescription
Fields []CollectionFieldDescription
Expand All @@ -198,6 +208,7 @@ func (c *CollectionDescription) UnmarshalJSON(bytes []byte) error {
c.ID = descMap.ID
c.RootID = descMap.RootID
c.SchemaVersionID = descMap.SchemaVersionID
c.IsMaterialized = descMap.IsMaterialized
c.Indexes = descMap.Indexes
c.Fields = descMap.Fields
c.Sources = make([]any, len(descMap.Sources))
Expand Down
8 changes: 8 additions & 0 deletions client/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ type Store interface {
transform immutable.Option[model.Lens],
) ([]CollectionDefinition, error)

// RefreshViews refreshes the caches of all views matching the given options. If no options are set all views
// will be refreshed.
//
// The cached result is dependent on the ACP settings of the source data and the permissions of the user making
// the call. At the moment only one cache can be active at a time, so please pay attention to access rights
// when making this call.
RefreshViews(context.Context, CollectionFetchOptions) error

// SetMigration sets the migration for all collections using the given source-destination schema version IDs.
//
// There may only be one migration per collection version. If another migration was registered it will be
Expand Down
26 changes: 26 additions & 0 deletions http/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,32 @@ func (c *Client) AddView(
return descriptions, nil
}

func (c *Client) RefreshViews(ctx context.Context, options client.CollectionFetchOptions) error {
methodURL := c.http.baseURL.JoinPath("view", "refresh")
params := url.Values{}
if options.Name.HasValue() {
params.Add("name", options.Name.Value())

Check warning on line 226 in http/client.go

View check run for this annotation

Codecov / codecov/patch

http/client.go#L226

Added line #L226 was not covered by tests
}
if options.SchemaVersionID.HasValue() {
params.Add("version_id", options.SchemaVersionID.Value())

Check warning on line 229 in http/client.go

View check run for this annotation

Codecov / codecov/patch

http/client.go#L229

Added line #L229 was not covered by tests
}
if options.SchemaRoot.HasValue() {
params.Add("schema_root", options.SchemaRoot.Value())

Check warning on line 232 in http/client.go

View check run for this annotation

Codecov / codecov/patch

http/client.go#L232

Added line #L232 was not covered by tests
}
if options.IncludeInactive.HasValue() {
params.Add("get_inactive", strconv.FormatBool(options.IncludeInactive.Value()))

Check warning on line 235 in http/client.go

View check run for this annotation

Codecov / codecov/patch

http/client.go#L235

Added line #L235 was not covered by tests
}
methodURL.RawQuery = params.Encode()

req, err := http.NewRequestWithContext(ctx, http.MethodPost, methodURL.String(), nil)
if err != nil {
return err

Check warning on line 241 in http/client.go

View check run for this annotation

Codecov / codecov/patch

http/client.go#L241

Added line #L241 was not covered by tests
}

_, err = c.http.request(req)
return err
}

func (c *Client) SetMigration(ctx context.Context, config client.LensConfig) error {
methodURL := c.http.baseURL.JoinPath("lens")

Expand Down
45 changes: 45 additions & 0 deletions http/handler_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,38 @@ func (s *storeHandler) GetSchema(rw http.ResponseWriter, req *http.Request) {
responseJSON(rw, http.StatusOK, schema)
}

func (s *storeHandler) RefreshViews(rw http.ResponseWriter, req *http.Request) {
store := req.Context().Value(dbContextKey).(client.Store)

options := client.CollectionFetchOptions{}
if req.URL.Query().Has("name") {
options.Name = immutable.Some(req.URL.Query().Get("name"))

Check warning on line 228 in http/handler_store.go

View check run for this annotation

Codecov / codecov/patch

http/handler_store.go#L228

Added line #L228 was not covered by tests
}
if req.URL.Query().Has("version_id") {
options.SchemaVersionID = immutable.Some(req.URL.Query().Get("version_id"))

Check warning on line 231 in http/handler_store.go

View check run for this annotation

Codecov / codecov/patch

http/handler_store.go#L231

Added line #L231 was not covered by tests
}
if req.URL.Query().Has("schema_root") {
options.SchemaRoot = immutable.Some(req.URL.Query().Get("schema_root"))

Check warning on line 234 in http/handler_store.go

View check run for this annotation

Codecov / codecov/patch

http/handler_store.go#L234

Added line #L234 was not covered by tests
}
if req.URL.Query().Has("get_inactive") {
getInactiveStr := req.URL.Query().Get("get_inactive")
var err error
getInactive, err := strconv.ParseBool(getInactiveStr)
if err != nil {
responseJSON(rw, http.StatusBadRequest, errorResponse{err})
return

Check warning on line 242 in http/handler_store.go

View check run for this annotation

Codecov / codecov/patch

http/handler_store.go#L237-L242

Added lines #L237 - L242 were not covered by tests
}
options.IncludeInactive = immutable.Some(getInactive)

Check warning on line 244 in http/handler_store.go

View check run for this annotation

Codecov / codecov/patch

http/handler_store.go#L244

Added line #L244 was not covered by tests
}

err := store.RefreshViews(req.Context(), options)
if err != nil {
responseJSON(rw, http.StatusBadRequest, errorResponse{err})
return

Check warning on line 250 in http/handler_store.go

View check run for this annotation

Codecov / codecov/patch

http/handler_store.go#L249-L250

Added lines #L249 - L250 were not covered by tests
}
rw.WriteHeader(http.StatusOK)
}

func (s *storeHandler) GetAllIndexes(rw http.ResponseWriter, req *http.Request) {
store := req.Context().Value(dbContextKey).(client.Store)

Expand Down Expand Up @@ -482,6 +514,18 @@ func (h *storeHandler) bindRoutes(router *Router) {
collectionDescribe.AddResponse(200, collectionsResponse)
collectionDescribe.Responses.Set("400", errorResponse)

viewRefresh := openapi3.NewOperation()
viewRefresh.OperationID = "view_refresh"
viewRefresh.Description = "Refresh view(s) by name, schema id, or version id."
viewRefresh.Tags = []string{"view"}
viewRefresh.AddParameter(collectionNameQueryParam)
viewRefresh.AddParameter(collectionSchemaRootQueryParam)
viewRefresh.AddParameter(collectionVersionIdQueryParam)
viewRefresh.AddParameter(collectionGetInactiveQueryParam)
viewRefresh.Responses = openapi3.NewResponses()
viewRefresh.Responses.Set("200", successResponse)
viewRefresh.Responses.Set("400", errorResponse)

patchCollection := openapi3.NewOperation()
patchCollection.OperationID = "patch_collection"
patchCollection.Description = "Update collection definitions"
Expand Down Expand Up @@ -609,6 +653,7 @@ func (h *storeHandler) bindRoutes(router *Router) {
router.AddRoute("/collections", http.MethodGet, collectionDescribe, h.GetCollection)
router.AddRoute("/collections", http.MethodPatch, patchCollection, h.PatchCollection)
router.AddRoute("/view", http.MethodPost, views, h.AddView)
router.AddRoute("/view/refresh", http.MethodPost, viewRefresh, h.RefreshViews)
router.AddRoute("/graphql", http.MethodGet, graphQLGet, h.ExecRequest)
router.AddRoute("/graphql", http.MethodPost, graphQLPost, h.ExecRequest)
router.AddRoute("/debug/dump", http.MethodGet, debugDump, h.PrintDump)
Expand Down
Loading

0 comments on commit 4b78c74

Please sign in to comment.