Skip to content

Commit c1ce30b

Browse files
committed
Add soft clear preserving oplog state
1 parent e15ce73 commit c1ce30b

File tree

2 files changed

+59
-4
lines changed

2 files changed

+59
-4
lines changed

crates/core/src/view_admin.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,21 @@ fn powersync_clear_impl(
150150
) -> Result<String, PowerSyncError> {
151151
let local_db = ctx.db_handle();
152152

153-
let clear_local = args[0].int();
153+
let flags = PowerSyncClearFlags(args[0].int());
154+
155+
if !flags.soft_clear() {
156+
// With a soft clear, we want to delete public data while keeping internal data around. When
157+
// connect() is called with compatible JWTs yielding a large overlap of buckets, this can
158+
// speed up the next sync.
159+
local_db.exec_safe("DELETE FROM ps_oplog; DELETE FROM ps_buckets")?;
160+
} else {
161+
local_db.exec_safe("UPDATE ps_buckets SET last_applied_op = 0")?;
162+
}
154163

155164
// language=SQLite
156165
local_db.exec_safe(
157166
"\
158-
DELETE FROM ps_oplog;
159167
DELETE FROM ps_crud;
160-
DELETE FROM ps_buckets;
161168
DELETE FROM ps_untyped;
162169
DELETE FROM ps_updated_rows;
163170
DELETE FROM ps_kv WHERE key != 'client_id';
@@ -166,7 +173,7 @@ DELETE FROM ps_stream_subscriptions;
166173
",
167174
)?;
168175

169-
let table_glob = if clear_local != 0 {
176+
let table_glob = if flags.clear_local() {
170177
"ps_data_*"
171178
} else {
172179
"ps_data__*"
@@ -199,6 +206,22 @@ DELETE FROM {table};",
199206
Ok(String::from(""))
200207
}
201208

209+
#[derive(Clone, Copy)]
210+
struct PowerSyncClearFlags(i32);
211+
212+
impl PowerSyncClearFlags {
213+
const MASK_CLEAR_LOCAL: i32 = 0x01;
214+
const MASK_SOFT_CLEAR: i32 = 0x02;
215+
216+
fn clear_local(self) -> bool {
217+
self.0 & Self::MASK_CLEAR_LOCAL != 0
218+
}
219+
220+
fn soft_clear(self) -> bool {
221+
self.0 & Self::MASK_SOFT_CLEAR != 0
222+
}
223+
}
224+
202225
create_auto_tx_function!(powersync_clear_tx, powersync_clear_impl);
203226
create_sqlite_text_fn!(powersync_clear, powersync_clear_tx, "powersync_clear");
204227

dart/test/sync_test.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,38 @@ void _syncTests<T>({
370370
expect(db.select('SELECT * FROM ps_sync_state'), hasLength(0));
371371
});
372372

373+
test('can soft clear', () {
374+
invokeControl('start', null);
375+
pushCheckpoint(buckets: [bucketDescription('a', count: 1)]);
376+
pushSyncData('a', '1', 'row-0', 'PUT', {'col': 'hi'});
377+
pushCheckpointComplete();
378+
379+
expect(db.select('SELECT * FROM items'), hasLength(1));
380+
381+
// Soft clear
382+
db.execute('SELECT powersync_clear(2)');
383+
db.select('select powersync_replace_schema(?)', [json.encode(testSchema)]);
384+
expect(db.select('SELECT * FROM items'), hasLength(0));
385+
386+
final request = invokeControl('start', null);
387+
expect(
388+
request,
389+
contains(containsPair(
390+
'EstablishSyncStream',
391+
{
392+
// Should request state from before clear
393+
'request': containsPair('buckets', [
394+
{'name': 'a', 'after': '1'}
395+
]),
396+
},
397+
)),
398+
);
399+
400+
pushCheckpoint(buckets: [bucketDescription('a', count: 1)]);
401+
pushCheckpointComplete();
402+
expect(db.select('SELECT * FROM items'), hasLength(1));
403+
});
404+
373405
test('persists download progress', () {
374406
const bucket = 'bkt';
375407
void expectProgress(int atLast, int sinceLast) {

0 commit comments

Comments
 (0)