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 }