go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/sqlite/conn_pool.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 sqlite
    26  
    27  import (
    28  	"sync"
    29  
    30  	"github.com/jmoiron/sqlx"
    31  
    32  	"go.temporal.io/server/common/config"
    33  	"go.temporal.io/server/common/resolver"
    34  )
    35  
    36  // This pool properly enabled the support for SQLite in the temporal server.
    37  // Internal Temporal services are highly isolated, each will create at least a single connection to the database violating
    38  // the SQLite concept of safety only within a single thread.
    39  type connPool struct {
    40  	mu   sync.Mutex
    41  	pool map[string]entry
    42  }
    43  
    44  type entry struct {
    45  	db       *sqlx.DB
    46  	refCount int
    47  }
    48  
    49  func newConnPool() *connPool {
    50  	return &connPool{
    51  		pool: make(map[string]entry),
    52  	}
    53  }
    54  
    55  // Allocate allocates the shared database in the pool or returns already exists instance with the same DSN. If instance
    56  // for such DSN already exists, it will be returned instead. Each request counts as reference until Close.
    57  func (cp *connPool) Allocate(
    58  	cfg *config.SQL,
    59  	resolver resolver.ServiceResolver,
    60  	create func(cfg *config.SQL, resolver resolver.ServiceResolver) (*sqlx.DB, error),
    61  ) (db *sqlx.DB, err error) {
    62  	cp.mu.Lock()
    63  	defer cp.mu.Unlock()
    64  
    65  	dsn, err := buildDSN(cfg)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	if entry, ok := cp.pool[dsn]; ok {
    71  		entry.refCount++
    72  		return entry.db, nil
    73  	}
    74  
    75  	db, err = create(cfg, resolver)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	cp.pool[dsn] = entry{db: db, refCount: 1}
    81  
    82  	return db, nil
    83  }
    84  
    85  // Close virtual connection to database. Only closes for real once no references left.
    86  func (cp *connPool) Close(cfg *config.SQL) {
    87  	cp.mu.Lock()
    88  	defer cp.mu.Unlock()
    89  
    90  	dsn, err := buildDSN(cfg)
    91  	if err != nil {
    92  		return
    93  	}
    94  
    95  	e, ok := cp.pool[dsn]
    96  	if !ok {
    97  		// no such database
    98  		return
    99  	}
   100  
   101  	e.refCount--
   102  	// todo: at the moment pool will persist a single connection to the DB for the whole duration of application
   103  	// temporal will start and stop DB connections multiple times, which will cause the loss of the cache
   104  	// and "db is closed" error
   105  	// if e.refCount == 0 {
   106  	// 	e.db.Close()
   107  	// 	delete(cp.pool, dsn)
   108  	// }
   109  }