diff --git a/drivers/pg/driver.go b/drivers/pg/driver.go index bbee76b5..62eced95 100644 --- a/drivers/pg/driver.go +++ b/drivers/pg/driver.go @@ -176,3 +176,13 @@ func (s *Driver) RefreshKinds(ctx context.Context) error { s.SchemaManager.kindIDsByKind = map[int16]graph.Kind{} return s.SchemaManager.Fetch(ctx) } + +func (s *Driver) Optimize(ctx context.Context, opts ...graph.OptimizeOption) error { + config := &graph.OptimizeConfig{} + for _, opt := range opts { + opt(config) + } + + // PostgreSQL VACUUM/ANALYZE implementation + return nil +} diff --git a/graph/optimize.go b/graph/optimize.go new file mode 100644 index 00000000..5a9e1150 --- /dev/null +++ b/graph/optimize.go @@ -0,0 +1,57 @@ +package graph + +import ( + "context" +) + +// TargetStorage identifies a region of graph storage that an optimization +// pass may target. +type TargetStorage int + +const ( + Nodes TargetStorage = iota + Relationships +) + +// OptimizeConfig is the resolved configuration for a single Optimize call. +// Drivers apply every OptimizeOption to this struct before reading it. +type OptimizeConfig struct { + // Targets is the set of storage regions to optimize. A nil or empty + // slice instructs the driver to optimize every region it knows about. + Targets []TargetStorage +} + +// OptimizeOption mutates an OptimizeConfig and is applied in the order +// passed to Optimize. +type OptimizeOption func(*OptimizeConfig) + +// OptimizeTargets restricts the optimization pass to the given storage +// regions. Repeated calls append targets rather than replacing them. +// Calling OptimizeTargets with no arguments does not change the resolved +// target set; if no targets are configured by the time Optimize runs, the +// driver treats that as "all regions it knows about". +func OptimizeTargets(targets ...TargetStorage) OptimizeOption { + return func(c *OptimizeConfig) { + c.Targets = append(c.Targets, targets...) + } +} + +// StorageMaintainer is an optional capability implemented by drivers that +// can perform storage maintenance. Drivers that cannot perform meaningful maintenance +// must not implement this interface; a failed type assertion is the +// signal for "this driver does not support storage maintenance". +// A non-nil error returned from Optimize signals a driver-specific failure +// during a supported call. +// +// Consumers detect support with a type assertion against a graph.Database: +// +// if m, ok := db.(graph.StorageMaintainer); ok { +// err := m.Optimize(ctx, graph.OptimizeTargets(graph.Nodes, graph.Relationships)) +// ... +// } +type StorageMaintainer interface { + // Optimize runs a storage maintenance pass against the regions + // identified by opts. With no options, every region the driver + // recognizes is optimized. + Optimize(ctx context.Context, opts ...OptimizeOption) error +} diff --git a/graph/optimize_test.go b/graph/optimize_test.go new file mode 100644 index 00000000..00a4867b --- /dev/null +++ b/graph/optimize_test.go @@ -0,0 +1,54 @@ +package graph_test + +import ( + "testing" + + "github.com/specterops/dawgs/graph" + "github.com/stretchr/testify/assert" +) + +func TestOptimizeOptions(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + opts []graph.OptimizeOption + want []graph.TargetStorage + }{ + { + name: "no options yields nil Targets", + opts: nil, + want: nil, + }, + { + name: "OptimizeTargets with no args leaves Targets nil", + opts: []graph.OptimizeOption{graph.OptimizeTargets()}, + want: nil, + }, + { + name: "single call preserves order", + opts: []graph.OptimizeOption{graph.OptimizeTargets(graph.Nodes, graph.Relationships)}, + want: []graph.TargetStorage{graph.Nodes, graph.Relationships}, + }, + { + name: "repeated calls accumulate", + opts: []graph.OptimizeOption{ + graph.OptimizeTargets(graph.Nodes), + graph.OptimizeTargets(graph.Relationships), + }, + want: []graph.TargetStorage{graph.Nodes, graph.Relationships}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + var cfg graph.OptimizeConfig + for _, o := range tc.opts { + o(&cfg) + } + assert.Equal(t, tc.want, cfg.Targets) + }) + } +}