diff --git a/docs/api-reference/apidocs.swagger.json b/docs/api-reference/apidocs.swagger.json index 4bc1815cc..dffa3ee69 100644 --- a/docs/api-reference/apidocs.swagger.json +++ b/docs/api-reference/apidocs.swagger.json @@ -3,7 +3,7 @@ "info": { "title": "Permify API", "description": "Permify is an open source authorization service for creating fine-grained and scalable authorization systems.", - "version": "v0.8.4", + "version": "v0.8.5", "contact": { "name": "API Support", "url": "https://github.com/Permify/permify/issues", diff --git a/go.mod b/go.mod index fcae4351d..7e41b2299 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/dgraph-io/ristretto v0.1.1 github.com/dustin/go-humanize v1.0.1 github.com/envoyproxy/protoc-gen-validate v1.0.4 + github.com/exaring/otelpgx v0.5.4 github.com/fatih/color v1.16.0 github.com/go-jose/go-jose/v3 v3.0.3 github.com/golang-jwt/jwt/v4 v4.5.0 @@ -67,7 +68,6 @@ require ( require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/distribution/reference v0.5.0 // indirect - github.com/exaring/otelpgx v0.5.4 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/goccy/go-json v0.10.2 // indirect @@ -79,6 +79,7 @@ require ( github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/moby/sys/user v0.1.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sethvargo/go-retry v0.2.4 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect ) diff --git a/go.sum b/go.sum index c9db99e7f..32c27a79b 100644 --- a/go.sum +++ b/go.sum @@ -325,8 +325,8 @@ github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3c github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= diff --git a/internal/config/config.go b/internal/config/config.go index c0bda95e6..cb520720d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -144,8 +144,14 @@ type ( // Database contains configuration for the database. Database struct { - Engine string `mapstructure:"engine"` // Database engine type (e.g., "postgres" or "memory") - URI string `mapstructure:"uri"` // Database connection URI + Engine string `mapstructure:"engine"` // Database engine type (e.g., "postgres" or "memory") + URI string `mapstructure:"uri"` // Database connection URI + Writer struct { + URI string `mapstructure:"uri"` + } `mapstructure:"writer"` + Reader struct { + URI string `mapstructure:"uri"` + } `mapstructure:"reader"` AutoMigrate bool `mapstructure:"auto_migrate"` // Whether to enable automatic migration MaxOpenConnections int `mapstructure:"max_open_connections"` // Maximum number of open connections to the database MaxIdleConnections int `mapstructure:"max_idle_connections"` // Maximum number of idle connections to the database diff --git a/internal/factories/database.go b/internal/factories/database.go index f06589636..8d5c9f3f8 100644 --- a/internal/factories/database.go +++ b/internal/factories/database.go @@ -32,17 +32,33 @@ import ( func DatabaseFactory(conf config.Database) (db database.Database, err error) { switch conf.Engine { case database.POSTGRES.String(): - db, err = PQDatabase.New(conf.URI, - PQDatabase.MaxOpenConnections(conf.MaxOpenConnections), - PQDatabase.MaxIdleConnections(conf.MaxIdleConnections), - PQDatabase.MaxConnectionIdleTime(conf.MaxConnectionIdleTime), - PQDatabase.MaxConnectionLifeTime(conf.MaxConnectionLifetime), - PQDatabase.WatchBufferSize(conf.WatchBufferSize), - PQDatabase.MaxDataPerWrite(conf.MaxDataPerWrite), - PQDatabase.MaxRetries(conf.MaxRetries), - ) - if err != nil { - return nil, err + + if conf.URI == "" { + db, err = PQDatabase.NewWithSeparateURIs(conf.Writer.URI, conf.Reader.URI, + PQDatabase.MaxOpenConnections(conf.MaxOpenConnections), + PQDatabase.MaxIdleConnections(conf.MaxIdleConnections), + PQDatabase.MaxConnectionIdleTime(conf.MaxConnectionIdleTime), + PQDatabase.MaxConnectionLifeTime(conf.MaxConnectionLifetime), + PQDatabase.WatchBufferSize(conf.WatchBufferSize), + PQDatabase.MaxDataPerWrite(conf.MaxDataPerWrite), + PQDatabase.MaxRetries(conf.MaxRetries), + ) + if err != nil { + return nil, err + } + } else { + db, err = PQDatabase.New(conf.URI, + PQDatabase.MaxOpenConnections(conf.MaxOpenConnections), + PQDatabase.MaxIdleConnections(conf.MaxIdleConnections), + PQDatabase.MaxConnectionIdleTime(conf.MaxConnectionIdleTime), + PQDatabase.MaxConnectionLifeTime(conf.MaxConnectionLifetime), + PQDatabase.WatchBufferSize(conf.WatchBufferSize), + PQDatabase.MaxDataPerWrite(conf.MaxDataPerWrite), + PQDatabase.MaxRetries(conf.MaxRetries), + ) + if err != nil { + return nil, err + } } // check postgres version diff --git a/internal/info.go b/internal/info.go index 98b3e8864..49dfdbddf 100644 --- a/internal/info.go +++ b/internal/info.go @@ -23,7 +23,7 @@ var Identifier = "" */ const ( // Version is the last release of the Permify (e.g. v0.1.0) - Version = "v0.8.4" + Version = "v0.8.5" ) // Function to create a single line of the ASCII art with centered content and color diff --git a/internal/storage/migration.go b/internal/storage/migration.go index 302db6584..c392a7d10 100644 --- a/internal/storage/migration.go +++ b/internal/storage/migration.go @@ -30,10 +30,19 @@ func Migrate(conf config.Database) (err error) { case database.POSTGRES.String(): // Create a new Postgres database connection var db *PQDatabase.Postgres - db, err = PQDatabase.New(conf.URI) - if err != nil { - return err + + if conf.URI == "" { + db, err = PQDatabase.NewWithSeparateURIs(conf.Writer.URI, conf.Reader.URI) + if err != nil { + return err + } + } else { + db, err = PQDatabase.New(conf.URI) + if err != nil { + return err + } } + // Ensure database connection is closed when function returns defer closeDB(db) diff --git a/internal/storage/postgres/bundleReader.go b/internal/storage/postgres/bundleReader.go index 25b34f9d6..88c800f3c 100644 --- a/internal/storage/postgres/bundleReader.go +++ b/internal/storage/postgres/bundleReader.go @@ -48,7 +48,7 @@ func (b *BundleReader) Read(ctx context.Context, tenantID, name string) (bundle slog.Debug("executing sql query", slog.Any("query", query), slog.Any("arguments", args)) var row pgx.Row - row = b.database.WritePool.QueryRow(ctx, query, args...) + row = b.database.ReadPool.QueryRow(ctx, query, args...) var jsonData string err = row.Scan(&jsonData) diff --git a/internal/storage/postgres/dataWriter.go b/internal/storage/postgres/dataWriter.go index 083e8df05..c98462278 100644 --- a/internal/storage/postgres/dataWriter.go +++ b/internal/storage/postgres/dataWriter.go @@ -3,7 +3,6 @@ package postgres import ( "context" "errors" - "fmt" "log/slog" "github.com/jackc/pgx/v5" @@ -184,7 +183,6 @@ func (w *DataWriter) write( var xid types.XID8 err = tx.QueryRow(ctx, utils.TransactionTemplate, tenantID).Scan(&xid) if err != nil { - fmt.Println("Transactions Insert =-=-=-=-=") return nil, err } @@ -232,13 +230,11 @@ func (w *DataWriter) write( tdquery, tdargs, err = tDeleteBuilder.ToSql() if err != nil { - fmt.Println("tDeleteBuilder ToSql =-=-=-=-=") return nil, err } _, err = tx.Exec(ctx, tdquery, tdargs...) if err != nil { - fmt.Println("tDeleteBuilder ExecContext =-=-=-=-=") return nil, err } @@ -247,13 +243,11 @@ func (w *DataWriter) write( tiquery, tiargs, err = tuplesInsertBuilder.ToSql() if err != nil { - fmt.Println("tuplesInsertBuilder ToSql =-=-=-=-=") return nil, err } _, err = tx.Exec(ctx, tiquery, tiargs...) if err != nil { - fmt.Println("tuplesInsertBuilder ExecContext =-=-=-=-=") return nil, err } } @@ -320,7 +314,6 @@ func (w *DataWriter) write( } if err = tx.Commit(ctx); err != nil { - fmt.Println("Commit =-=-=-=-=") return nil, err } diff --git a/pkg/cmd/config.go b/pkg/cmd/config.go index b4343e37e..4cf1de49a 100644 --- a/pkg/cmd/config.go +++ b/pkg/cmd/config.go @@ -71,6 +71,8 @@ func NewConfigCommand() *cobra.Command { f.String("service-permission-cache-max-cost", conf.Service.Permission.Cache.MaxCost, "permission service cache max cost") f.String("database-engine", conf.Database.Engine, "data source. e.g. postgres, memory") f.String("database-uri", conf.Database.URI, "uri of your data source to store relation tuples and schema") + f.String("database-writer-uri", conf.Database.Writer.URI, "writer uri of your data source to store relation tuples and schema") + f.String("database-reader-uri", conf.Database.Reader.URI, "reader uri of your data source to store relation tuples and schema") f.Bool("database-auto-migrate", conf.Database.AutoMigrate, "auto migrate database tables") f.Int("database-max-open-connections", conf.Database.MaxOpenConnections, "maximum number of parallel connections that can be made to the database at any time") f.Int("database-max-idle-connections", conf.Database.MaxIdleConnections, "maximum number of idle connections that can be made to the database at any time") @@ -174,6 +176,8 @@ func conf() func(cmd *cobra.Command, args []string) error { // DATABASE []string{"database.engine", cfg.Database.Engine, getKeyOrigin(cmd, "database-engine", "PERMIFY_DATABASE_ENGINE")}, []string{"database.uri", HideSecret(cfg.Database.URI), getKeyOrigin(cmd, "database-uri", "PERMIFY_DATABASE_URI")}, + []string{"database.writer.uri", HideSecret(cfg.Database.Writer.URI), getKeyOrigin(cmd, "database-writer-uri", "PERMIFY_DATABASE_WRITER_URI")}, + []string{"database.reader.uri", HideSecret(cfg.Database.Reader.URI), getKeyOrigin(cmd, "database-reader-uri", "PERMIFY_DATABASE_READER_URI")}, []string{"database.auto_migrate", fmt.Sprintf("%v", cfg.Database.AutoMigrate), getKeyOrigin(cmd, "database-auto-migrate", "PERMIFY_DATABASE_AUTO_MIGRATE")}, []string{"database.max_open_connections", fmt.Sprintf("%v", cfg.Database.MaxOpenConnections), getKeyOrigin(cmd, "database-max-open-connections", "PERMIFY_DATABASE_MAX_OPEN_CONNECTIONS")}, []string{"database.max_idle_connections", fmt.Sprintf("%v", cfg.Database.MaxIdleConnections), getKeyOrigin(cmd, "database-max-idle-connections", "PERMIFY_DATABASE_MAX_IDLE_CONNECTIONS")}, diff --git a/pkg/cmd/flags/serve.go b/pkg/cmd/flags/serve.go index 924ac84d6..9763ee9d6 100644 --- a/pkg/cmd/flags/serve.go +++ b/pkg/cmd/flags/serve.go @@ -346,6 +346,20 @@ func RegisterServeFlags(flags *pflag.FlagSet) { panic(err) } + if err = viper.BindPFlag("database.writer.uri", flags.Lookup("database-writer-uri")); err != nil { + panic(err) + } + if err = viper.BindEnv("database.writer.uri", "PERMIFY_DATABASE_WRITER_URI"); err != nil { + panic(err) + } + + if err = viper.BindPFlag("database.reader.uri", flags.Lookup("database-reader-uri")); err != nil { + panic(err) + } + if err = viper.BindEnv("database.reader.uri", "PERMIFY_DATABASE_READER_URI"); err != nil { + panic(err) + } + if err = viper.BindPFlag("database.auto_migrate", flags.Lookup("database-auto-migrate")); err != nil { panic(err) } diff --git a/pkg/cmd/serve.go b/pkg/cmd/serve.go index 8de2d423a..d98422972 100644 --- a/pkg/cmd/serve.go +++ b/pkg/cmd/serve.go @@ -102,6 +102,8 @@ func NewServeCommand() *cobra.Command { f.String("service-permission-cache-max-cost", conf.Service.Permission.Cache.MaxCost, "permission service cache max cost") f.String("database-engine", conf.Database.Engine, "data source. e.g. postgres, memory") f.String("database-uri", conf.Database.URI, "uri of your data source to store relation tuples and schema") + f.String("database-writer-uri", conf.Database.Writer.URI, "writer uri of your data source to store relation tuples and schema") + f.String("database-reader-uri", conf.Database.Reader.URI, "reader uri of your data source to store relation tuples and schema") f.Bool("database-auto-migrate", conf.Database.AutoMigrate, "auto migrate database tables") f.Int("database-max-open-connections", conf.Database.MaxOpenConnections, "maximum number of parallel connections that can be made to the database at any time") f.Int("database-max-idle-connections", conf.Database.MaxIdleConnections, "maximum number of idle connections that can be made to the database at any time") diff --git a/pkg/database/postgres/postgres.go b/pkg/database/postgres/postgres.go index a7c04566f..3ce490884 100644 --- a/pkg/database/postgres/postgres.go +++ b/pkg/database/postgres/postgres.go @@ -35,8 +35,18 @@ type Postgres struct { maxIdleConnections int } -// New - Creates new postgresql db instance +// New - func New(uri string, opts ...Option) (*Postgres, error) { + return newDB(uri, uri, opts...) +} + +// NewWithSeparateURIs - +func NewWithSeparateURIs(writerUri, readerUri string, opts ...Option) (*Postgres, error) { + return newDB(writerUri, readerUri, opts...) +} + +// new - Creates new postgresql db instance +func newDB(writerUri, readerUri string, opts ...Option) (*Postgres, error) { pg := &Postgres{ maxOpenConnections: _defaultMaxOpenConnections, maxIdleConnections: _defaultMaxIdleConnections, @@ -52,12 +62,12 @@ func New(uri string, opts ...Option) (*Postgres, error) { pg.Builder = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) - writeConfig, err := pgxpool.ParseConfig(uri) + writeConfig, err := pgxpool.ParseConfig(writerUri) if err != nil { return nil, err } - readConfig, err := pgxpool.ParseConfig(uri) + readConfig, err := pgxpool.ParseConfig(readerUri) if err != nil { return nil, err } diff --git a/pkg/pb/base/v1/openapi.pb.go b/pkg/pb/base/v1/openapi.pb.go index 56cc4a81e..b6e45e8b5 100644 --- a/pkg/pb/base/v1/openapi.pb.go +++ b/pkg/pb/base/v1/openapi.pb.go @@ -46,7 +46,7 @@ var file_base_v1_openapi_proto_rawDesc = []byte{ 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x66, 0x79, 0x2f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x66, 0x79, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, - 0x32, 0x06, 0x76, 0x30, 0x2e, 0x38, 0x2e, 0x34, 0x2a, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, + 0x32, 0x06, 0x76, 0x30, 0x2e, 0x38, 0x2e, 0x35, 0x2a, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x5a, 0x23, 0x0a, 0x21, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, diff --git a/proto/base/v1/openapi.proto b/proto/base/v1/openapi.proto index 65b014d0e..ae9480720 100644 --- a/proto/base/v1/openapi.proto +++ b/proto/base/v1/openapi.proto @@ -9,7 +9,7 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { title: "Permify API"; description: "Permify is an open source authorization service for creating fine-grained and scalable authorization systems."; - version: "v0.8.4"; + version: "v0.8.5"; contact: { name: "API Support"; url: "https://github.com/Permify/permify/issues";