Skip to content

K8SPG-699: Add pgvector support #991

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8352,6 +8352,8 @@ spec:
type: boolean
pgStatStatements:
type: boolean
pgvector:
type: boolean
type: object
image:
description: |-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8064,6 +8064,8 @@ spec:
type: boolean
pg_stat_monitor:
type: boolean
pgvector:
type: boolean
type: object
custom:
items:
Expand Down
2 changes: 2 additions & 0 deletions config/crd/bases/pgv2.percona.com_perconapgclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8470,6 +8470,8 @@ spec:
type: boolean
pg_stat_monitor:
type: boolean
pgvector:
type: boolean
type: object
custom:
items:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8312,6 +8312,8 @@ spec:
type: boolean
pgStatStatements:
type: boolean
pgvector:
type: boolean
type: object
image:
description: |-
Expand Down
4 changes: 4 additions & 0 deletions deploy/bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8763,6 +8763,8 @@ spec:
type: boolean
pg_stat_monitor:
type: boolean
pgvector:
type: boolean
type: object
custom:
items:
Expand Down Expand Up @@ -33985,6 +33987,8 @@ spec:
type: boolean
pgStatStatements:
type: boolean
pgvector:
type: boolean
type: object
image:
description: |-
Expand Down
1 change: 1 addition & 0 deletions deploy/cr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ spec:
# builtin:
# pg_stat_monitor: true
# pg_audit: true
# pgvector: false
# custom:
# - name: pg_cron
# version: 1.6.1
4 changes: 4 additions & 0 deletions deploy/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8763,6 +8763,8 @@ spec:
type: boolean
pg_stat_monitor:
type: boolean
pgvector:
type: boolean
type: object
custom:
items:
Expand Down Expand Up @@ -33985,6 +33987,8 @@ spec:
type: boolean
pgStatStatements:
type: boolean
pgvector:
type: boolean
type: object
image:
description: |-
Expand Down
4 changes: 4 additions & 0 deletions deploy/cw-bundle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8763,6 +8763,8 @@ spec:
type: boolean
pg_stat_monitor:
type: boolean
pgvector:
type: boolean
type: object
custom:
items:
Expand Down Expand Up @@ -33985,6 +33987,8 @@ spec:
type: boolean
pgStatStatements:
type: boolean
pgvector:
type: boolean
type: object
image:
description: |-
Expand Down
15 changes: 14 additions & 1 deletion internal/controller/postgrescluster/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/percona/percona-postgresql-operator/internal/pgaudit"
"github.com/percona/percona-postgresql-operator/internal/pgstatmonitor"
"github.com/percona/percona-postgresql-operator/internal/pgstatstatements"
"github.com/percona/percona-postgresql-operator/internal/pgvector"
"github.com/percona/percona-postgresql-operator/internal/postgis"
"github.com/percona/percona-postgresql-operator/internal/postgres"
pgpassword "github.com/percona/percona-postgresql-operator/internal/postgres/password"
Expand Down Expand Up @@ -234,7 +235,7 @@ func (r *Reconciler) reconcilePostgresDatabases(
// Calculate a hash of the SQL that should be executed in PostgreSQL.

// K8SPG-375, K8SPG-577
var pgAuditOK, pgStatMonitorOK, pgStatStatementsOK, postgisInstallOK bool
var pgAuditOK, pgStatMonitorOK, pgStatStatementsOK, pgvectorOK, postgisInstallOK bool
create := func(ctx context.Context, exec postgres.Executor) error {
if cluster.Spec.Extensions.PGStatMonitor {
if pgStatMonitorOK = pgstatmonitor.EnableInPostgreSQL(ctx, exec) == nil; !pgStatMonitorOK {
Expand Down Expand Up @@ -280,6 +281,18 @@ func (r *Reconciler) reconcilePostgresDatabases(
}
}

if cluster.Spec.Extensions.PGVector {
if pgvectorOK = pgvector.EnableInPostgreSQL(ctx, exec) == nil; !pgvectorOK {
r.Recorder.Event(cluster, corev1.EventTypeWarning, "pgvectorDisabled",
"Unable to install pgvector")
}
} else {
if pgvectorOK = pgvector.DisableInPostgreSQL(ctx, exec) == nil; !pgvectorOK {
r.Recorder.Event(cluster, corev1.EventTypeWarning, "pgvectorEnabled",
"Unable to disable pgvector")
}
}

// Enabling PostGIS extensions is a one-way operation
// e.g., you can take a PostgresCluster and turn it into a PostGISCluster,
// but you cannot reverse the process, as that would potentially remove an extension
Expand Down
48 changes: 48 additions & 0 deletions internal/pgvector/postgres.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package pgvector

import (
"context"

"github.com/percona/percona-postgresql-operator/internal/logging"
"github.com/percona/percona-postgresql-operator/internal/postgres"
)

// EnableInPostgreSQL installs pgvector triggers into every database.
func EnableInPostgreSQL(ctx context.Context, exec postgres.Executor) error {
log := logging.FromContext(ctx)

stdout, stderr, err := exec.ExecInAllDatabases(ctx,
// Quiet the NOTICE from IF EXISTS, and install the pgAudit event triggers.
// - https://www.postgresql.org/docs/current/runtime-config-client.html
// - https://github.com/pgaudit/pgaudit#settings
`SET client_min_messages = WARNING; CREATE EXTENSION IF NOT EXISTS vector; ALTER EXTENSION vector UPDATE;`,
map[string]string{
"ON_ERROR_STOP": "on", // Abort when any one command fails.
"QUIET": "on", // Do not print successful commands to stdout.
})

log.V(1).Info("enabled pgvector", "stdout", stdout, "stderr", stderr)

return err
}

func DisableInPostgreSQL(ctx context.Context, exec postgres.Executor) error {
log := logging.FromContext(ctx)

stdout, stderr, err := exec.ExecInAllDatabases(ctx,
// Quiet the NOTICE from IF EXISTS, and install the pgAudit event triggers.
// - https://www.postgresql.org/docs/current/runtime-config-client.html
// - https://github.com/pgaudit/pgaudit#settings
`SET client_min_messages = WARNING; DROP EXTENSION IF EXISTS vector;`,
map[string]string{
"ON_ERROR_STOP": "on", // Abort when any one command fails.
"QUIET": "on", // Do not print successful commands to stdout.
})

log.V(1).Info("disabled pgvector", "stdout", stdout, "stderr", stderr)

return err
}

// PostgreSQLParameters sets the parameters required by pgAudit.
func PostgreSQLParameters(outParameters *postgres.Parameters) {}
61 changes: 61 additions & 0 deletions internal/pgvector/postgres_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package pgvector

import (
"context"
"errors"
"io"
"strings"
"testing"

"gotest.tools/v3/assert"
)

func TestEnableInPostgreSQL(t *testing.T) {
expected := errors.New("whoops")
exec := func(
_ context.Context, stdin io.Reader, stdout, stderr io.Writer, command ...string,
) error {
assert.Assert(t, stdout != nil, "should capture stdout")
assert.Assert(t, stderr != nil, "should capture stderr")

assert.Assert(t, strings.Contains(strings.Join(command, "\n"),
`SELECT datname FROM pg_catalog.pg_database`,
), "expected all databases and templates")

b, err := io.ReadAll(stdin)
assert.NilError(t, err)
assert.Equal(t, string(b), strings.Trim(`
SET client_min_messages = WARNING; CREATE EXTENSION IF NOT EXISTS vector; ALTER EXTENSION vector UPDATE;
`, "\t\n"))

return expected
}

ctx := context.Background()
assert.Equal(t, expected, EnableInPostgreSQL(ctx, exec))
}

func TestDisableInPostgreSQL(t *testing.T) {
expected := errors.New("whoops")
exec := func(
_ context.Context, stdin io.Reader, stdout, stderr io.Writer, command ...string,
) error {
assert.Assert(t, stdout != nil, "should capture stdout")
assert.Assert(t, stderr != nil, "should capture stderr")

assert.Assert(t, strings.Contains(strings.Join(command, "\n"),
`SELECT datname FROM pg_catalog.pg_database`,
), "expected all databases and templates")

b, err := io.ReadAll(stdin)
assert.NilError(t, err)
assert.Equal(t, string(b), strings.Trim(`
SET client_min_messages = WARNING; DROP EXTENSION IF EXISTS vector;
`, "\t\n"))

return expected
}

ctx := context.Background()
assert.Equal(t, expected, DisableInPostgreSQL(ctx, exec))
}
6 changes: 6 additions & 0 deletions pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func (cr *PerconaPGCluster) Default() {
cr.Spec.Proxy.PGBouncer.Metadata.Labels[LabelOperatorVersion] = cr.Spec.CRVersion

t := true
f := false

if cr.Spec.Backups.TrackLatestRestorableTime == nil {
cr.Spec.Backups.TrackLatestRestorableTime = &t
Expand All @@ -224,6 +225,9 @@ func (cr *PerconaPGCluster) Default() {
if cr.Spec.Extensions.BuiltIn.PGAudit == nil {
cr.Spec.Extensions.BuiltIn.PGAudit = &t
}
if cr.Spec.Extensions.BuiltIn.PGVector == nil {
cr.Spec.Extensions.BuiltIn.PGVector = &f
}

if cr.CompareVersion("2.6.0") >= 0 && cr.Spec.AutoCreateUserSchema == nil {
cr.Spec.AutoCreateUserSchema = &t
Expand Down Expand Up @@ -344,6 +348,7 @@ func (cr *PerconaPGCluster) ToCrunchy(ctx context.Context, postgresCluster *crun

postgresCluster.Spec.Extensions.PGStatMonitor = *cr.Spec.Extensions.BuiltIn.PGStatMonitor
postgresCluster.Spec.Extensions.PGAudit = *cr.Spec.Extensions.BuiltIn.PGAudit
postgresCluster.Spec.Extensions.PGVector = *cr.Spec.Extensions.BuiltIn.PGVector

return postgresCluster, nil
}
Expand Down Expand Up @@ -567,6 +572,7 @@ type CustomExtensionsStorageSpec struct {
type BuiltInExtensionsSpec struct {
PGStatMonitor *bool `json:"pg_stat_monitor,omitempty"`
PGAudit *bool `json:"pg_audit,omitempty"`
PGVector *bool `json:"pgvector,omitempty"`
}

type ExtensionsSpec struct {
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/pgv2.percona.com/v2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ type ExtensionsSpec struct {
PGStatMonitor bool `json:"pgStatMonitor,omitempty"`
PGAudit bool `json:"pgAudit,omitempty"`
PGStatStatements bool `json:"pgStatStatements,omitempty"`
PGVector bool `json:"pgvector,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use camelCase for pgvector as we have for other extensions, like pgVector?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the official style: https://github.com/pgvector/pgvector

}

// DataSource defines data sources for a new PostgresCluster.
Expand Down
Loading