mattermost/server/public/pluginapi/store.go
Agniva De Sarker 89f5a0deec
MM-56402: Initialize replica conn pool on-demand (#26410)
Previously, we would setup both pools only when
GetMasterDB was called. This was inefficient and
would waste open connections if the replica wasn't used
at all.

We fix it to initialize the pools as they are called.

https://mattermost.atlassian.net/browse/MM-56402
```release-note
NONE
```

Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
2024-03-08 13:11:48 +05:30

124 lines
2.5 KiB
Go

package pluginapi
import (
"database/sql"
"sync"
// import sql drivers
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/shared/driver"
"github.com/pkg/errors"
)
// StoreService exposes the underlying database.
type StoreService struct {
initializedMaster bool
initializedReplica bool
api plugin.API
driver plugin.Driver
mutex sync.Mutex
masterDB *sql.DB
replicaDB *sql.DB
}
// GetMasterDB gets the master database handle.
//
// Minimum server version: 5.16
func (s *StoreService) GetMasterDB() (*sql.DB, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
if err := s.initializeMaster(); err != nil {
return nil, err
}
return s.masterDB, nil
}
// GetReplicaDB gets the replica database handle.
// Returns masterDB if a replica is not configured.
//
// Minimum server version: 5.16
func (s *StoreService) GetReplicaDB() (*sql.DB, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
if err := s.initializeReplica(); err != nil {
return nil, err
}
if s.replicaDB != nil {
return s.replicaDB, nil
}
return s.masterDB, nil
}
// Close closes any open resources. This method is idempotent.
func (s *StoreService) Close() error {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.replicaDB != nil {
if err := s.replicaDB.Close(); err != nil {
return err
}
}
if s.masterDB != nil {
if err := s.masterDB.Close(); err != nil {
return err
}
}
return nil
}
// DriverName returns the driver name for the datasource.
func (s *StoreService) DriverName() string {
return *s.api.GetConfig().SqlSettings.DriverName
}
func (s *StoreService) initializeMaster() error {
if s.initializedMaster {
return nil
}
if s.driver == nil {
return errors.New("no db driver was provided")
}
// Set up master db
db := sql.OpenDB(driver.NewConnector(s.driver, true /* IsMaster */))
if err := db.Ping(); err != nil {
return errors.Wrap(err, "failed to connect to master db")
}
s.masterDB = db
s.initializedMaster = true
return nil
}
func (s *StoreService) initializeReplica() error {
if s.initializedReplica {
return nil
}
config := s.api.GetUnsanitizedConfig()
// Set up replica db
if len(config.SqlSettings.DataSourceReplicas) > 0 {
db := sql.OpenDB(driver.NewConnector(s.driver, false /* IsMaster */))
if err := db.Ping(); err != nil {
return errors.Wrap(err, "failed to connect to replica db")
}
s.replicaDB = db
}
s.initializedReplica = true
return nil
}