go.temporal.io/server@v1.23.0/common/persistence/sql/factory.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package sql
    26  
    27  import (
    28  	"fmt"
    29  	"sync"
    30  
    31  	"go.temporal.io/server/common/config"
    32  	"go.temporal.io/server/common/log"
    33  	p "go.temporal.io/server/common/persistence"
    34  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    35  	"go.temporal.io/server/common/resolver"
    36  )
    37  
    38  type (
    39  	// Factory vends store objects backed by MySQL
    40  	Factory struct {
    41  		cfg         config.SQL
    42  		mainDBConn  DbConn
    43  		clusterName string
    44  		logger      log.Logger
    45  	}
    46  
    47  	// DbConn represents a logical mysql connection - its a
    48  	// wrapper around the standard sql connection pool with
    49  	// additional reference counting
    50  	DbConn struct {
    51  		dbKind   sqlplugin.DbKind
    52  		cfg      *config.SQL
    53  		resolver resolver.ServiceResolver
    54  
    55  		sqlplugin.DB
    56  
    57  		sync.Mutex
    58  		refCnt int
    59  	}
    60  )
    61  
    62  // NewFactory returns an instance of a factory object which can be used to create
    63  // datastores backed by any kind of SQL store
    64  func NewFactory(
    65  	cfg config.SQL,
    66  	r resolver.ServiceResolver,
    67  	clusterName string,
    68  	logger log.Logger,
    69  ) *Factory {
    70  	return &Factory{
    71  		cfg:         cfg,
    72  		clusterName: clusterName,
    73  		logger:      logger,
    74  		mainDBConn:  NewRefCountedDBConn(sqlplugin.DbKindMain, &cfg, r),
    75  	}
    76  }
    77  
    78  // GetDB return a new SQL DB connection
    79  func (f *Factory) GetDB() (sqlplugin.DB, error) {
    80  	conn, err := f.mainDBConn.Get()
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	return conn, err
    85  }
    86  
    87  // NewTaskStore returns a new task store
    88  func (f *Factory) NewTaskStore() (p.TaskStore, error) {
    89  	conn, err := f.mainDBConn.Get()
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	return newTaskPersistence(conn, f.cfg.TaskScanPartitions, f.logger)
    94  }
    95  
    96  // NewShardStore returns a new shard store
    97  func (f *Factory) NewShardStore() (p.ShardStore, error) {
    98  	conn, err := f.mainDBConn.Get()
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return newShardPersistence(conn, f.clusterName, f.logger)
   103  }
   104  
   105  // NewMetadataStore returns a new metadata store
   106  func (f *Factory) NewMetadataStore() (p.MetadataStore, error) {
   107  	conn, err := f.mainDBConn.Get()
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	return newMetadataPersistenceV2(conn, f.clusterName, f.logger)
   112  }
   113  
   114  // NewClusterMetadataStore returns a new ClusterMetadata store
   115  func (f *Factory) NewClusterMetadataStore() (p.ClusterMetadataStore, error) {
   116  	conn, err := f.mainDBConn.Get()
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	return newClusterMetadataPersistence(conn, f.logger)
   121  }
   122  
   123  // NewExecutionStore returns a new ExecutionStore
   124  func (f *Factory) NewExecutionStore() (p.ExecutionStore, error) {
   125  	conn, err := f.mainDBConn.Get()
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	return NewSQLExecutionStore(conn, f.logger)
   130  }
   131  
   132  // NewQueue returns a new queue backed by sql
   133  func (f *Factory) NewQueue(queueType p.QueueType) (p.Queue, error) {
   134  	conn, err := f.mainDBConn.Get()
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	return newQueue(conn, f.logger, queueType)
   140  }
   141  
   142  // NewQueueV2 returns a new data-access object for queues and messages.
   143  func (f *Factory) NewQueueV2() (p.QueueV2, error) {
   144  	conn, err := f.mainDBConn.Get()
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	return NewQueueV2(conn, f.logger), nil
   149  }
   150  
   151  // Close closes the factory
   152  func (f *Factory) Close() {
   153  	f.mainDBConn.ForceClose()
   154  }
   155  
   156  // NewRefCountedDBConn returns a  logical mysql connection that
   157  // uses reference counting to decide when to close the
   158  // underlying connection object. The reference count gets incremented
   159  // everytime get() is called and decremented everytime Close() is called
   160  func NewRefCountedDBConn(
   161  	dbKind sqlplugin.DbKind,
   162  	cfg *config.SQL,
   163  	r resolver.ServiceResolver,
   164  ) DbConn {
   165  	return DbConn{
   166  		dbKind:   dbKind,
   167  		cfg:      cfg,
   168  		resolver: r,
   169  	}
   170  }
   171  
   172  // Get returns a mysql db connection and increments a reference count
   173  // this method will create a new connection, if an existing connection
   174  // does not exist
   175  func (c *DbConn) Get() (sqlplugin.DB, error) {
   176  	c.Lock()
   177  	defer c.Unlock()
   178  	if c.refCnt == 0 {
   179  		conn, err := NewSQLDB(c.dbKind, c.cfg, c.resolver)
   180  		if err != nil {
   181  			return nil, err
   182  		}
   183  		c.DB = conn
   184  	}
   185  	c.refCnt++
   186  	return c, nil
   187  }
   188  
   189  // ForceClose ignores reference counts and shutsdown the underlying connection pool
   190  func (c *DbConn) ForceClose() {
   191  	c.Lock()
   192  	defer c.Unlock()
   193  	if c.DB != nil {
   194  		err := c.DB.Close()
   195  		if err != nil {
   196  			fmt.Println("failed to close database connection, may leak some connection", err)
   197  		}
   198  	}
   199  	c.refCnt = 0
   200  }
   201  
   202  // Close closes the underlying connection if the reference count becomes zero
   203  func (c *DbConn) Close() error {
   204  	c.Lock()
   205  	defer c.Unlock()
   206  	c.refCnt--
   207  	if c.refCnt == 0 {
   208  		return c.DB.Close()
   209  	}
   210  	return nil
   211  }