summaryrefslogtreecommitdiff
path: root/internal/db/postgres.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/db/postgres.go')
-rw-r--r--internal/db/postgres.go91
1 files changed, 74 insertions, 17 deletions
diff --git a/internal/db/postgres.go b/internal/db/postgres.go
index 67d0bb4e9..f4cf47406 100644
--- a/internal/db/postgres.go
+++ b/internal/db/postgres.go
@@ -23,31 +23,69 @@ import (
"errors"
"fmt"
"net/url"
+ "time"
"github.com/go-fed/activity/streams/vocab"
"github.com/go-pg/pg"
+ "github.com/sirupsen/logrus"
)
type postgresService struct {
config *Config
conn *pg.DB
- ready bool
+ log *logrus.Entry
+ cancel context.CancelFunc
}
// newPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface.
// Under the hood, it uses https://github.com/go-pg/pg to create and maintain a database connection.
-func newPostgresService(config *Config) (*postgresService, error) {
+func newPostgresService(ctx context.Context, config *Config, log *logrus.Entry) (*postgresService, error) {
opts, err := derivePGOptions(config)
if err != nil {
return nil, fmt.Errorf("could not create postgres service: %s", err)
}
- conn := pg.Connect(opts)
+
+ readyChan := make(chan interface{})
+ opts.OnConnect = func(c *pg.Conn) error {
+ close(readyChan)
+ return nil
+ }
+
+ // create a connection
+ pgCtx, cancel := context.WithCancel(ctx)
+ conn := pg.Connect(opts).WithContext(pgCtx)
+
+ // actually *begin* the connection so that we can tell if the db is there
+ // and listening, and also trigger the opts.OnConnect function passed in above
+ tx, err := conn.Begin()
+ if err != nil {
+ cancel()
+ return nil, fmt.Errorf("db connection error: %s", err)
+ }
+
+ // close the transaction we just started so it doesn't hang around
+ if err := tx.Rollback(); err != nil {
+ cancel()
+ return nil, fmt.Errorf("db connection error: %s", err)
+ }
+
+ // make sure the opts.OnConnect function has been triggered
+ // and closed the ready channel
+ select {
+ case <-readyChan:
+ log.Infof("postgres connection ready")
+ case <-time.After(5 * time.Second):
+ cancel()
+ return nil, errors.New("db connection timeout")
+ }
+
+ // we can confidently return this useable postgres service now
return &postgresService{
- config,
- conn,
- false,
+ config: config,
+ conn: conn,
+ log: log,
+ cancel: cancel,
}, nil
-
}
/*
@@ -68,22 +106,35 @@ func derivePGOptions(config *Config) (*pg.Options, error) {
}
// validate address
- address := config.Address
- if address == "" {
- return nil, errors.New("address not provided")
+ if config.Address == "" {
+ config.Address = defaultAddress
+ }
+ if !hostnameRegex.MatchString(config.Address) && !ipv4Regex.MatchString(config.Address) && config.Address != "localhost" {
+ return nil, fmt.Errorf("address %s was neither an ipv4 address nor a valid hostname", config.Address)
+ }
+
+ // validate username
+ if config.User == "" {
+ config.User = postgresDefaultUser
}
- if !hostnameRegex.MatchString(address) && !ipv4Regex.MatchString(address) {
- return nil, fmt.Errorf("address %s was neither an ipv4 address nor a valid hostname", address)
+
+ // validate that there's a password
+ if config.Password == "" {
+ return nil, errors.New("no password set")
+ }
+
+ // validate database
+ if config.Database == "" {
+ config.Database = defaultDatabase
}
+ // We can rely on the pg library we're using to set
+ // sensible defaults for everything we don't set here.
options := &pg.Options{
Addr: fmt.Sprintf("%s:%d", config.Address, config.Port),
User: config.User,
Password: config.Password,
Database: config.Database,
- OnConnect: func(c *pg.Conn) error {
- return nil
- },
}
return options, nil
@@ -176,6 +227,12 @@ func (ps *postgresService) Liked(c context.Context, actorIRI *url.URL) (follower
EXTRA FUNCTIONS
*/
-func (ps *postgresService) Ready() bool {
- return false
+func (ps *postgresService) Stop(ctx context.Context) error {
+ ps.log.Info("closing db connection")
+ if err := ps.conn.Close(); err != nil {
+ // only cancel if there's a problem closing the db
+ ps.cancel()
+ return err
+ }
+ return nil
}