-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathPowerSyncDatabase.kt
More file actions
280 lines (261 loc) · 10.3 KB
/
Copy pathPowerSyncDatabase.kt
File metadata and controls
280 lines (261 loc) · 10.3 KB
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
package com.powersync
import co.touchlab.kermit.Logger
import com.powersync.bucket.StreamPriority
import com.powersync.connectors.PowerSyncBackendConnector
import com.powersync.db.ActiveDatabaseGroup
import com.powersync.db.ActiveDatabaseResource
import com.powersync.db.PowerSyncDatabaseImpl
import com.powersync.db.Queries
import com.powersync.db.crud.CrudBatch
import com.powersync.db.crud.CrudTransaction
import com.powersync.db.driver.SQLiteConnectionPool
import com.powersync.db.schema.Schema
import com.powersync.sync.SyncOptions
import com.powersync.sync.SyncStatus
import com.powersync.sync.SyncStream
import com.powersync.utils.JsonParam
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlin.coroutines.cancellation.CancellationException
/**
* A PowerSync managed database.
*
* Use one instance per database file.
*
* Use [PowerSyncDatabase.connect] to connect to the PowerSync service, to keep the local database in sync with the remote database.
*
* All changes to local tables are automatically recorded, whether connected or not. Once connected, the changes are uploaded.
*/
public interface PowerSyncDatabase : Queries {
/**
* Indicates if the PowerSync client has been closed.
* A new client is required after a client has been closed.
*/
public val closed: Boolean
/**
* Identifies the database client.
* This is typically the database name.
*/
public val identifier: String
/**
* The current sync status.
*/
public val currentStatus: SyncStatus
/**
* Replace the schema with a new version. This is for advanced use cases - typically the schema
* should just be specified once in the constructor.
*
* Cannot be used while connected - this should only be called before connect.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun updateSchema(schema: Schema)
/**
* Suspend function that resolves when the first sync has occurred
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun waitForFirstSync()
/**
* Suspend function that resolves when the first sync covering at least all buckets with the
* given [priority] (or a higher one, since those would be synchronized first) has completed.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun waitForFirstSync(priority: StreamPriority)
/**
* Connect to the PowerSync service, and keep the databases in sync.
*
* The connection is automatically re-opened if it fails for any reason.
*
* Use @param [connector] to specify the [PowerSyncBackendConnector].
* Use @param [crudThrottleMs] to specify the time between CRUD operations. Defaults to 1000ms.
* Use @param [retryDelayMs] to specify the delay between retries after failure. Defaults to 5000ms.
* Use @param [params] to specify sync parameters from the client.
* Use @param [appMetadata] to specify application metadata that will be displayed in PowerSync service logs.
*
* Example usage:
* ```
* val params = JsonParam.Map(
* mapOf(
* "name" to JsonParam.String("John Doe"),
* "age" to JsonParam.Number(30),
* "isStudent" to JsonParam.Boolean(false)
* )
* )
*
* val appMetadata = mapOf(
* "appVersion" to "1.0.0",
* "deviceId" to "device456"
* )
*
* connect(
* connector = connector,
* crudThrottleMs = 2000L,
* retryDelayMs = 10000L,
* params = params,
* appMetadata = appMetadata
* )
* ```
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun connect(
connector: PowerSyncBackendConnector,
crudThrottleMs: Long = 1000L,
retryDelayMs: Long = 5000L,
params: Map<String, JsonParam?> = emptyMap(),
options: SyncOptions = SyncOptions(),
appMetadata: Map<String, String> = emptyMap(),
)
/**
* Get a batch of crud data to upload.
*
* Returns null if there is no data to upload.
*
* Use this from the [PowerSyncBackendConnector.uploadData]` callback.
*
* Once the data have been successfully uploaded, call [CrudBatch.complete] before
* requesting the next batch.
*
* Use [limit] to specify the maximum number of updates to return in a single
* batch. Default is 100.
*
* This method does include transaction ids in the result, but does not group
* data by transaction. One batch may contain data from multiple transactions,
* and a single transaction may be split over multiple batches.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun getCrudBatch(limit: Int = 100): CrudBatch?
/**
* Get the next recorded transaction to upload.
*
* Returns null if there is no data to upload.
*
* Use this from the [PowerSyncBackendConnector.uploadData] callback.
*
* Once the data have been successfully uploaded, call [CrudTransaction.complete] before
* requesting the next transaction.
*
* Unlike [getCrudBatch], this only returns data from a single transaction at a time.
* All data for the transaction is loaded into memory.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun getNextCrudTransaction(): CrudTransaction? = getCrudTransactions().firstOrNull()
/**
* Obtains a flow emitting completed transactions with local writes against the database.
* This is typically used from the [PowerSyncBackendConnector.uploadData] callback.
* Each entry emitted by the returned flow is a full transaction containing all local writes
* made while that transaction was active.
*
* Unlike [getNextCrudTransaction], which always returns the oldest transaction that hasn't
* been [CrudTransaction.complete]d yet, this flow can be used to collect multiple transactions.
* Calling [CrudTransaction.complete] will mark that and all prior transactions emitted by the
* flow as completed.
*
* This can be used to upload multiple transactions in a single batch, e.g with:
*
* ```Kotlin
* val batch = mutableListOf<CrudEntry>()
* var lastTx: CrudTransaction? = null
*
* database.getCrudTransactions().takeWhile { batch.size < 100 }.collect {
* batch.addAll(it.crud)
* lastTx = it
* }
*
* if (batch.isNotEmpty()) {
* uploadChanges(batch)
* lastTx!!.complete(null)
* }
* ````
*
* If there is no local data to upload, returns an empty flow.
*/
public fun getCrudTransactions(): Flow<CrudTransaction>
/**
* Convenience method to get the current version of PowerSync.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun getPowerSyncVersion(): String
/**
* Create a [SyncStream] instance for the given [name] and [parameters].
*
* Use [SyncStream.subscribe] on the returned instance to subscribe to the stream.
*/
public fun syncStream(
name: String,
parameters: Map<String, JsonParam>? = null,
): SyncStream
/**
* Close the sync connection.
*
* Use [connect] to connect again.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun disconnect()
/**
* Disconnect and clear the database.
*
* Clearing the database is useful when a user logs out, to ensure another user logging in later would not see
* previous data.
*
* The database can still be queried after this is called, but the tables
* would be empty.
*
* To preserve data in local-only tables, set clearLocal to false.
*
* A "soft" clear deletes publicly visible tables, but keeps internal copies of data synced in the database. This
* usually means that if the same user logs out and back in again, the first sync is very fast because all internal
* data is still available. When a different user logs in, no old data would be visible at any point.
* Using soft deletes is recommended where it's not a security issue that old data could be reconstructible from the
* database.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun disconnectAndClear(
clearLocal: Boolean = true,
soft: Boolean = false,
)
/**
* Close the database, releasing resources.
* Also disconnects any active connection.
*
* Once close is called, this database cannot be used again - a new one must be constructed.
*/
@Throws(PowerSyncException::class, CancellationException::class)
public suspend fun close()
public companion object PowerSyncOpenFactory {
/**
* Creates a PowerSync database managed by an external connection pool.
*
* In this case, PowerSync will not open its own SQLite connections, but rather refer to
* connections in the [pool].
*
* The `identifier` parameter should be a name identifying the path of the database. The
* PowerSync SDK will emit a warning if multiple databases are opened with the same
* identifier, and uses internal locks to ensure these two databases are not synced at the
* same time (which would be inefficient and can cause consistency issues).
*/
public fun opened(
pool: SQLiteConnectionPool,
scope: CoroutineScope,
schema: Schema,
identifier: String,
logger: Logger,
): PowerSyncDatabase {
val group = ActiveDatabaseGroup.referenceDatabase(logger, identifier)
return openedWithGroup(pool, scope, schema, logger, group)
}
internal fun openedWithGroup(
pool: SQLiteConnectionPool,
scope: CoroutineScope,
schema: Schema,
logger: Logger,
group: Pair<ActiveDatabaseResource, Any>,
): PowerSyncDatabase =
PowerSyncDatabaseImpl(
schema,
scope,
pool,
logger,
group,
)
}
}