snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
149
internal/persistence/sql/persister.go
Normal file
149
internal/persistence/sql/persister.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright © 2023 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/ory/pop/v6"
|
||||
"github.com/ory/x/contextx"
|
||||
"github.com/ory/x/logrusx"
|
||||
"github.com/ory/x/otelx"
|
||||
"github.com/ory/x/popx"
|
||||
|
||||
keysetpagination "github.com/ory/x/pagination/keysetpagination_v2"
|
||||
"github.com/ory/x/pagination/paginationplanner"
|
||||
|
||||
"github.com/ory/keto/internal/driver/config"
|
||||
"github.com/ory/keto/internal/persistence"
|
||||
)
|
||||
|
||||
type (
|
||||
Persister struct {
|
||||
conn *pop.Connection
|
||||
d dependencies
|
||||
nid uuid.UUID
|
||||
|
||||
planner *paginationplanner.PaginationPlanner
|
||||
}
|
||||
dependencies interface {
|
||||
logrusx.Provider
|
||||
otelx.Provider
|
||||
contextx.Provider
|
||||
config.Provider
|
||||
|
||||
PopConnection(ctx context.Context) (*pop.Connection, error)
|
||||
MapperNamespace(ctx context.Context) uuid.UUID
|
||||
NewShardID() uuid.UUID
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed migrations/sql/*.sql
|
||||
Migrations embed.FS
|
||||
|
||||
_ persistence.Persister = &Persister{}
|
||||
)
|
||||
|
||||
// Pagination Planner for relation_tuple table
|
||||
var (
|
||||
t = paginationplanner.NewTable()
|
||||
TupleColNamespace = t.NewColumn("namespace")
|
||||
TupleColObject = t.NewColumn("object")
|
||||
TupleColRel = t.NewColumn("relation")
|
||||
TupleColSubjectId = t.NewColumn("subject_id")
|
||||
TupleColSubjectSetNamespace = t.NewColumn("subject_set_namespace")
|
||||
TupleColSubjectSetObject = t.NewColumn("subject_set_object")
|
||||
TupleColSubjectSetRel = t.NewColumn("subject_set_relation")
|
||||
)
|
||||
|
||||
var (
|
||||
fullIdxPlan = paginationplanner.PaginationPlan{
|
||||
DefaultPageToken: keysetpagination.NewPageToken([]keysetpagination.Column{
|
||||
{Name: TupleColNamespace.Name(), Order: keysetpagination.OrderAscending, Value: ""},
|
||||
{Name: TupleColObject.Name(), Order: keysetpagination.OrderAscending, Value: uuid.Nil},
|
||||
{Name: TupleColRel.Name(), Order: keysetpagination.OrderAscending, Value: ""},
|
||||
{Name: TupleColSubjectId.Name(), Order: keysetpagination.OrderAscending, Nullable: true},
|
||||
{Name: TupleColSubjectSetNamespace.Name(), Order: keysetpagination.OrderAscending, Nullable: true},
|
||||
{Name: TupleColSubjectSetObject.Name(), Order: keysetpagination.OrderAscending, Nullable: true},
|
||||
{Name: TupleColSubjectSetRel.Name(), Order: keysetpagination.OrderAscending, Nullable: true},
|
||||
{Name: "commit_time", Order: keysetpagination.OrderAscending, Value: "1970-01-01"},
|
||||
{Name: "shard_id", Order: keysetpagination.OrderAscending, Value: uuid.Nil},
|
||||
}...),
|
||||
ApplicableQueries: [][]paginationplanner.Column{
|
||||
{},
|
||||
{TupleColNamespace},
|
||||
{TupleColNamespace, TupleColObject},
|
||||
{TupleColNamespace, TupleColObject, TupleColRel},
|
||||
},
|
||||
}
|
||||
|
||||
fallbackPlan = paginationplanner.PaginationPlan{
|
||||
DefaultPageToken: keysetpagination.NewPageToken([]keysetpagination.Column{
|
||||
{Name: "shard_id", Order: keysetpagination.OrderAscending, Value: uuid.Nil},
|
||||
}...),
|
||||
}
|
||||
)
|
||||
|
||||
func NewPersister(ctx context.Context, reg dependencies, nid uuid.UUID) (*Persister, error) {
|
||||
conn, err := reg.PopConnection(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
planner, err := paginationplanner.NewPaginationPlanner(fallbackPlan, []paginationplanner.PaginationPlan{fullIdxPlan})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &Persister{
|
||||
d: reg,
|
||||
nid: nid,
|
||||
conn: conn,
|
||||
planner: planner,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Persister) Connection(ctx context.Context) *pop.Connection {
|
||||
return popx.GetConnection(ctx, p.conn.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (p *Persister) queryWithNetwork(ctx context.Context) *pop.Query {
|
||||
return p.Connection(ctx).Where("nid = ?", p.NetworkID(ctx))
|
||||
}
|
||||
|
||||
func (p *Persister) Transaction(ctx context.Context, f func(ctx context.Context) error) error {
|
||||
return popx.Transaction(ctx, p.conn.WithContext(ctx), func(ctx context.Context, _ *pop.Connection) error { return f(ctx) })
|
||||
}
|
||||
|
||||
func (p *Persister) WithLatestSnapshot(ctx context.Context, f func(ctx context.Context) error) error {
|
||||
conn := p.conn.WithContext(ctx)
|
||||
|
||||
// Reuse an existing transaction rather than nesting.
|
||||
if popx.GetConnection(ctx, conn).TX != nil {
|
||||
return f(ctx)
|
||||
}
|
||||
|
||||
// REPEATABLE READ pins a consistent snapshot so all queries in the check see
|
||||
// the same database state. ReadOnly enforces read-only at the DB level.
|
||||
// CockroachDB promotes that level to SERIALIZABLE; SQLite ignores
|
||||
// the isolation level entirely.
|
||||
return popx.TransactionWithOptions(ctx, conn, &sql.TxOptions{
|
||||
Isolation: sql.LevelRepeatableRead,
|
||||
ReadOnly: true,
|
||||
}, func(ctx context.Context, _ *pop.Connection) error {
|
||||
return f(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Persister) NetworkID(ctx context.Context) uuid.UUID {
|
||||
return p.d.Contextualizer().Network(ctx, p.nid)
|
||||
}
|
||||
|
||||
func (p *Persister) SetNetwork(nid uuid.UUID) {
|
||||
p.nid = nid
|
||||
}
|
||||
Reference in New Issue
Block a user