-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathConnectionPool.h
155 lines (128 loc) · 4.84 KB
/
ConnectionPool.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include "ConnectionState.h"
#include "JSIHelper.h"
#include "sqlite3.h"
#include <string>
#include <vector>
#include <future>
#ifndef ConnectionPool_h
#define ConnectionPool_h
enum TransactionEvent { COMMIT, ROLLBACK };
struct TransactionCallbackPayload {
std::string *dbName;
TransactionEvent event;
};
// The number of concurrent read connections to the database.
/**
* Concurrent connection pool class.
*
* This allows for multiple (currently READ_CONNECTIONS) read connections and a
* single write connection to operate concurrently.
*
* The SQLite database connections are opened in WAL mode, allowing for
* concurrent reads and writes.
*
* This class acts as a simple state manager allowing requests for read and
* write connections. The class will queue requests for connections and will
* notify them they become available.
*
* Operations requesting locks here are synchronous and should be executed on a
* single thread, however once a lock is active the connections can be used in a
* thread pool for async statement executions.
*
* Synchronization, callback queueing and executions are managed by the
* JavaScript portion of the library.
*
* The general flow is:
* + JavaScript requests read/write lock on a connection via
* readLock/writeLock. JS provides a unique context ID for the lock
* ------> If a connection is available: The SQLite connection is locked to the
* ....... connection lock context ID provided by the requestor. The JavaScript
* ....... bridge is informed from the Connection pool that the requested
* ....... context lock ID is now active and SQL requests can be made with the
* ....... context ID (on the relevant connection).
* ------> If no connections are available, the context ID is added to a FIFO
* ....... queue. Once other requests are completed the JavaScript bridge is
* ....... informed that the requested context lock ID is now active.
*
* + Any SQL requests are triggered (at the correct time) from the JavaScript
* callback. Those requests are synchronized by returning JSI promises over the
* bridge.
*
* + The JavaScript bridge makes a request to release the lock on the connection
* pool once it's async callback operations are either resolved or rejected.
*/
class ConnectionPool {
private:
int maxReads;
std::string dbName;
ConnectionState **readConnections;
ConnectionState writeConnection;
std::vector<ConnectionLockId> readQueue;
std::vector<ConnectionLockId> writeQueue;
// Cached constant payloads for c style commit/rollback callbacks
const TransactionCallbackPayload commitPayload;
const TransactionCallbackPayload rollbackPayload;
void (*onContextCallback)(std::string, ConnectionLockId);
void (*onCommitCallback)(const TransactionCallbackPayload *);
bool isConcurrencyEnabled;
public:
ConnectionPool(std::string dbName, std::string docPath,
unsigned int numReadConnections);
~ConnectionPool();
friend int onCommitIntermediate(ConnectionPool *pool);
/**
* Add a task to the read queue. If there are no available connections,
* the task will be queued.
*/
void readLock(ConnectionLockId contextId);
/**
* Add a task to the write queue.
*/
void writeLock(ConnectionLockId contextId);
/**
* Queue in context
*/
SQLiteOPResult queueInContext(ConnectionLockId contextId,
std::function<void(sqlite3 *)> task);
/**
* Callback function when a new context is available for use
*/
void setOnContextAvailable(void (*callback)(std::string, ConnectionLockId));
/**
* Set a callback function for table updates
*/
void setTableUpdateHandler(void (*callback)(void *, int, const char *,
const char *, sqlite3_int64));
/**
* Set a callback function for transaction commits/rollbacks
*/
void setTransactionFinalizerHandler(
void (*callback)(const TransactionCallbackPayload *));
/**
* Close a context in order to progress queue
*/
void closeContext(ConnectionLockId contextId);
// void closeContext(ConnectionLockContext *context);
/**
* Close all connections.
*/
void closeAll();
/**
* Refreshes the schema for all connections.
*/
std::future<void> refreshSchema();
/**
* Attaches another database to all connections
*/
SQLiteOPResult attachDatabase(std::string const dbFileName,
std::string const docPath,
std::string const alias);
SQLiteOPResult detachDatabase(std::string const alias);
private:
std::vector<ConnectionState *> getAllConnections();
void activateContext(ConnectionState &state, ConnectionLockId contextId);
SQLiteOPResult genericSqliteOpenDb(string const dbName, string const docPath,
sqlite3 **db, int sqlOpenFlags);
};
int onCommitIntermediate(ConnectionPool *pool);
#endif