Skip to content

Commit

Permalink
db: use triggers and change table for reactivity on native (#4279)
Browse files Browse the repository at this point in the history
* db: fix reactivity issue, use triggers on both platforms, reduce code duplication

* Switch to using commitHook/callback function to process changes rather than polling

* switch to using updateHook to trigger processChangs() on native

* Stop syncing thread unreads on channel focus, this triggers unnecessary inserts/updates to the db. Stop adding duplicate posts to the flush queue in the change listener. Stop adding posts that were just inserted to the flush queue, we only want to invalidate if it's an update to an existing post.

* Add syncChannelThreadUnreads back in, but don't insert unless we're certain we have new unreads

* Fix merge artefact

* update to new type for op-sqlite db connection
  • Loading branch information
patosullivan authored Jan 9, 2025
1 parent f794ee1 commit 9036269
Show file tree
Hide file tree
Showing 10 changed files with 384 additions and 382 deletions.
164 changes: 82 additions & 82 deletions apps/tlon-mobile/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1757,39 +1757,39 @@ SPEC CHECKSUMS:
boost: d3f49c53809116a5d38da093a8aa78bf551aed09
BranchSDK: cb046c2714b03e573484ce9e349e2ddbad7016e8
DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953
EASClient: 45d2459af3eb8a0283b50d3de38af7ac9930d747
EXApplication: 45ce4be704dbde177c98e2e0e38c3aae3bb92535
EXAV: e160fec51bf6bc41c3b6f7e4c72ea9fd36860cd6
EXConstants: 348adb88fb0d65892f16732ec5e02e1365c31588
EXFont: db0560db6676fce70dcc7656483d6560be02cbf0
EXImageLoader: ba2506b443b9656e93167c104406a2c265924823
EASClient: a42ee8bf36c93b3128352faf2ae49405ab4f80bd
EXApplication: 137189a3f149b4e8e546884629392c3efc94cbd3
EXAV: e4f6137431ddc4cb025895046bfefa9612025c35
EXConstants: 988aa430ca0f76b43cd46b66e7fae3287f9cc2fc
EXFont: 21b9c760abd593ce8f0d5386b558ced76018506f
EXImageLoader: 55080616b2fe9da19ef8c7f706afd9814e279b6b
EXJSONUtils: 5c42959e87be238b045ef37cc5268b16a6c0ad4a
EXManifests: 429136cffa3ae82d1ba3b60b7243fb186615562e
EXMediaLibrary: d3bf5c458643f9821b96af328d399ef3f44de7db
EXNotifications: 0df7db27e8cc862e37a30a56775ab883642f2872
Expo: 72deced93cbf0d42e434764bb432e010ff4d8bdb
expo-dev-client: 90dd9b69d09a3a3788746df8713252f65125a18b
expo-dev-launcher: 0a8a761b63f1702cb513939198f1be0bcebdb235
expo-dev-menu: 81becfb05d38e4bdcd50bfeec31617ee08567d5e
expo-dev-menu-interface: 44e69ddff62bbc6c5418c200e657635720b5a480
ExpoBattery: 6cdcb673b99f53b3e9955b03268fd571cff68f51
ExpoBlur: 3a9548a738624968836926f4aa1e18fa22155640
ExpoClipboard: 24cc2b881ab6ca2e5b431b1f6d9d4a302adbf9d0
ExpoDevice: a9a3955ece7cffe8ee98869d01018e4d6ca91bc7
ExpoFileSystem: df58e1eb2a4d6f1006a1ca70bddfbbf63e52fa4f
ExpoHaptics: c91902e436f3fb0e07aa19acc118018089fa90de
ExpoImage: a70db90f39a7af98930cef91c84e877b1131f3dd
ExpoImageManipulator: 0c2ada7a028619ea1cc0c670bfa90c8ebeaa4af4
ExpoImagePicker: d06822d74f1f0e7fe7cb070ece0fff6e678fa3eb
ExpoKeepAwake: 3b8cf8533b9212500565a1e41fb080fc5af29918
ExpoLinearGradient: 501f9bbd83f3ec1d0e0425862b9ef4693605fc1c
ExpoLocalization: bee500d73d9bd5694c70d1e09d62f15d96cd0977
ExpoModulesCore: 29b3765cf833c60c0a742e8b0693316105af81df
ExpoSecureStore: 4cec57fd2c40dcff05ce2186c39afc1af39d213c
EXSplashScreen: 7bbe853200d944b7266b53d5c79499b0d1fdcb19
EXManifests: 5e8c29f36c716af768a4ea47ec05e1b89ab93091
EXMediaLibrary: 70cf1fb7028fda2d682090c9fe57568674e4abab
EXNotifications: e11f0e9a5b657c064a481a5d522f3bc5a07bf7cd
Expo: fb745b3074989670b6641f9f20463e8ee56a69ca
expo-dev-client: dbc8e8a81d17a9d92e083a2856d056ba9a58984d
expo-dev-launcher: 346abfe8bd102bdcf6605dee01380a97635b63dc
expo-dev-menu: 166fc9c7b82641cdead1dc26d958d20a127ee97b
expo-dev-menu-interface: 7ba029c9d1a82ac22b9b584c00514860b060553e
ExpoBattery: 60bf880aea8f769fe39f709a920442542c1bfd62
ExpoBlur: e832d874bd94afc0645daddbd3162ec1ce172080
ExpoClipboard: b597982124f067ff9f5b89093eb3d97898d5d877
ExpoDevice: d204395e17fffdcefa7470bdef33b07719ac41b1
ExpoFileSystem: 74cc0fae916f9f044248433971dcfc8c3befd057
ExpoHaptics: 28a771b630353cd6e8dcf1b1e3e693e38ad7c3c3
ExpoImage: 8cf2d51de3d03b7e984e9b0ba8f19c0c22057001
ExpoImageManipulator: c1d7cb865eacd620a35659f3da34c70531f10b59
ExpoImagePicker: 66970181d1c838f444e5e1f81b804ab2d5ff49bd
ExpoKeepAwake: 0f5cad99603a3268e50af9a6eb8b76d0d9ac956c
ExpoLinearGradient: 4ad1449a2408e0435ac959076562b3921f2e32a1
ExpoLocalization: f5f5d71dc0c9514d3d77b2771144f6fed6398d04
ExpoModulesCore: 2346e83abf90c2b2c16a54c3fc4a1169ae12816c
ExpoSecureStore: c84ae37d1c36f38524d289c67c3a2e3fc56f1108
EXSplashScreen: 6bd596128cd52fac91997ebc64f3d394c843a8f9
EXStructuredHeaders: 5b0f47259db047dc1fdfa84752e292c2bfa68ecd
EXTaskManager: c3da046549b52b572a9bddb140e2b3d0e28b2ece
EXUpdates: 8cc7407328c3852e3ce890a381fe0022ae71902b
EXTaskManager: 3e446dbf75cd662aa6e7d6828be5bc26265241c3
EXUpdates: 40e069f2987861a6d39101c4496e816561f3e167
EXUpdatesInterface: 3e444e2093e25b7ca0999a7d8c16e8392dee70c3
FBLazyVector: 98c189b92292d4bfeac13ffa8df3ce3d84e2fc5b
FBReactNativeSpec: f40d89f4be3e854b08cf9b66cba9e9d6c68d863d
Expand All @@ -1816,81 +1816,81 @@ SPEC CHECKSUMS:
libvmaf: 27f523f1e63c694d14d534cd0fddd2fab0ae8711
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
nanopb: 438bc412db1928dac798aa6fd75726007be04262
op-sqlite: b0c2f06e0660235bc3dca10714b0498fdbb811bb
op-sqlite: 12039198fdf455a3af0e18420b656c23cf8ecc01
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
RCT-Folly: cd21f1661364f975ae76b3308167ad66b09f53f5
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: d362a61864a64315aee00faea8dee6cf5b3f4aad
RCTTypeSafety: 09baf60faeab02492dc8bf04ce5af1dda645b86d
ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979
React: b87c7c7c12f8232bd7cfdc4a00bf687144c17e30
React-callinvoker: 67de0bc05ecb7e690345a53a1661cea9b24670b0
React-Codegen: e81f008b53f3db74336a4324ea2b1ca15d7fe2da
React-Core: 4cb660fe310d6ee6d1e87308c2bec1541e1a40b4
React-CoreModules: 12f9b33b8e1c67324be550e3c2ef1a03fa1aa473
React-cxxreact: 4abb46b2ce1fa4eabe1105be8df331fc2dca9706
React-Codegen: dd60c11cfeb20cdd8b5a787603346d4e04f95499
React-Core: 4c87a1873c6d11c6d3843582fbc266ba9ea304ce
React-CoreModules: 29ad1cbe757a70575913457bb7c646b7f4d4edf0
React-cxxreact: 08ffaf2def6fe1ec8ef7f16e208587c96a87978e
React-debug: 30d97e8abfec9b6ba0ad111b92ac749d3ace2dd5
React-Fabric: 93d15dfd53ea0a8ece4d4bc399924571c1ab2d1c
React-FabricImage: 3e7fa7e5bd260008741e60f7ca4f8d5417b5fa7f
React-graphics: fe19a38958a23f951ef36613a34a6e867a6ef8f9
React-hermes: 1f47d1f8b218565fd980eb00121d40e0ba2edcea
React-ImageManager: 941a294408b8a35cf929cf38d74deb83522c4556
React-jserrorhandler: c06aabbbcd49aad9ddaa360b1fedaa7a4a0e820e
React-jsi: 0422dc0b680234b3eabee952c20e23ca63e10b3d
React-jsiexecutor: a61443a46b9616f17142d8f63d2e788f9b634be6
React-Fabric: 8da8e4c0ed603fbed5208f5045cd1582a96ffa3d
React-FabricImage: f0cf2b8a823e98dae20013910bbdd0889bc719c2
React-graphics: 6dc8dba8a095a962204dd8bd4ca8264a1ed718db
React-hermes: fdaab14cc289d8d9cd45ffd9a3f8aa11157d4c7e
React-ImageManager: 4a39d60f9163fce83474c2b79acec8336742983d
React-jserrorhandler: 81e19a227a75741456c8f7b1240fc8926fb48c7e
React-jsi: 2253621cb2fb5d43a78fec4db8989cf9711039df
React-jsiexecutor: 5e4620a87fbc4ab174d75220f06ba8b53ae8317b
React-jsinspector: aee04d04ef553d5e30e52a4de2af958cb060069f
React-logger: ef76a6d8e04672f19be9b3a49f6ecc4c7141399b
React-Mapbuffer: 30deaceab523707646f5f35efad961133e4e1f25
react-native-branch: 5d4ecda7bae040542f6c9f651a05d3df23d8b35a
react-native-context-menu-view: c7477ad2a9b5005eb45dd168c2dcdd64526dba77
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
react-native-netinfo: 5364263f903da576bdef9c84a76fe243ab06812c
react-native-safe-area-context: 435f4c13ac75ceed6135382ee77d57d1a5b5b2d6
react-native-webview: dc76301066fed23a66b1198f990056d2f8c67d20
React-logger: 87a4232dd55485435edfa6803ff0de0b5c9eea1a
React-Mapbuffer: 94db5977cd64330f9e715c19026c9a267d8358a5
react-native-branch: 021b9c261f732d0950e9a304284779d48bf81109
react-native-context-menu-view: dcec18eb8882e20596dbb75802e7d19cb87dac02
react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06
react-native-netinfo: 3aa5637c18834966e0c932de8ae1ae56fea20a97
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
react-native-webview: ad868affda04ff2b204de83546193bc0325a8280
React-nativeconfig: 44cd3076b158c39cb6758f238cd3e65953e989c0
React-NativeModulesApple: d8f0d34218e897ff56525e4446ba4d5e8f74f9c8
React-NativeModulesApple: af58ca346cf42c3bb4dab9173b6edd0c83c2a21c
React-perflogger: c93b6a895eca3f9196656bb20ce0e15ad597a4e9
React-RCTActionSheet: 258842f426709dccbc2af31ca42b0a1807d76ad7
React-RCTAnimation: e173f27b6e20108354df11ee0ae5a9f7fe09930f
React-RCTAppDelegate: 8c1e725f4be6d5fb55e7ea62b76c929eddcb859e
React-RCTBlob: 2135cb24f3fa9f4617d7cae03aee10724d5122bb
React-RCTFabric: 51dbbcfa09f76183d2fe9d305eedf0f4f7343333
React-RCTImage: 345d59868e372e90a40f30f9775c8f34c50b5ebb
React-RCTLinking: d9623fb24075a5a7b9d5f263297135c5128ea37c
React-RCTNetwork: 4dfc12857609eae588c212268656bf0ff3ebe1f3
React-RCTSettings: 5fa0803b17f29d87dc8d4649a1e5a32d4d081237
React-RCTText: f23cca90ce571720460ee8e3525ff7e5f1af5ad0
React-RCTVibration: efd2a82f8ecac6e7b363689322215735d1cbcf9e
React-rendererdebug: ea0f77385485b5251a0e61133e3b5f709ef02e9b
React-RCTAnimation: 78c40269e35864f541b7486d17bd82a353c99fbc
React-RCTAppDelegate: d98ec2cdfb161d7a8496990e9649f94018025922
React-RCTBlob: 593a5dbc58c45e3cccc37ad4498b10766ace26e6
React-RCTFabric: c589babe0224cb137f64bfa4770e92d752c18012
React-RCTImage: a0cdbb81db012ebc42c7dbaabdcb15f488c7c391
React-RCTLinking: 82b6b0a5b2d5c8d3a28997e70bda46bac4be4c6e
React-RCTNetwork: 45e30079bcb987724028c9a93c0110b6d82e4a1f
React-RCTSettings: e67cbe694fe45b080b96b1394d4e45dff1b3ae04
React-RCTText: 14a54686a1fa0b51b76660c7700980fdec6c3093
React-RCTVibration: 00561f3d12dca44ed55af9060752bf8cf3fb0bfc
React-rendererdebug: 9e62f84756f080d88ed01b8063355c3a042934ed
React-rncore: e7f10bc6dbd75fa137583bd3b2bc4880203dbc1d
React-runtimeexecutor: bf98e8973ed4c45139fbbaf2c34af44053acc9a9
React-runtimescheduler: e6288bce4309ba16280ce54690b4d7109091ff43
React-utils: 28bc17e3e21dc06646b45a003718e0fe6034c66f
ReactCommon: e7f772a7660fc683a6ba0a0e0bebd45f4977ee57
recaptcha-enterprise-react-native: 82f186a0ab29c91c4d4bd08f7758f849e91b4558
React-runtimescheduler: 7a4ccc1854c5918dee508536fad93172b4c49629
React-utils: 59bbef368c9ba8b6e27416b46933cd03a35f2841
ReactCommon: 656e520d76937c8d781ef82a7186a4af7160d814
recaptcha-enterprise-react-native: 7d63c5bdde3b48996b984a86ac2b536a1d8f5f16
RecaptchaEnterprise: dc302910b77963a0cc6f6908407e30b35268a755
RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21
RNCAsyncStorage: a03b770a50541a761447cea9c24536047832124d
RNCClipboard: c20b93d3a1b47ce86dcffa5c0af5dd59938e47e1
RNDeviceInfo: addb9b427c2822a2d8e94c87a136a224e0af738c
RNFBApp: e905ce948d9290555d9ab1303449827eb5119b5c
RNFBCrashlytics: e9459c8656ccbbb53d12afe47b4a96194c858745
RNFBPerf: ae6cfdac3e06eab3d4d092944e979e95a69b0179
RNFlashList: 1076a3fb7c4608a8cdf265f0783592b8fc41b6a7
RNGestureHandler: 9852b7617e28551f5d339309c73ebf221ba1013d
RNReanimated: 1b07d2def747e4e5510ecd8460b2862029ee3df2
RNScreens: 1174f55dd2f72abe45a4e48bf20e552feb051600
RNSVG: a9e095acf2e207f2ef491870523ed455636cf3b8
RNCAsyncStorage: 618d03a5f52fbccb3d7010076bc54712844c18ef
RNCClipboard: 090462274cc05b02628bd158baf6d73c3abe8441
RNDeviceInfo: db5c64a060e66e5db3102d041ebe3ef307a85120
RNFBApp: 91311b27bc9a33e23b76a62825afd1635501018a
RNFBCrashlytics: c3219ef7a0c779f2428236215781c38e7892f6f9
RNFBPerf: 2c926ff255c704a644dd53572008cba47c67ada0
RNFlashList: 4b4b6b093afc0df60ae08f9cbf6ccd4c836c667a
RNGestureHandler: 79c035e2243d3b7f4f353e7eb32869eace6b596c
RNReanimated: 4b1bce37b188450e3a2d03c925edd5fb11d4bb2d
RNScreens: a4d9ce8f68f833f4e42410140eafd88e38bba163
RNSVG: 3f65a03e0c61a8495dee92bf82545ed9041cbf3b
SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9
SDWebImageAVIFCoder: 8348fef6d0ec69e129c66c9fe4d74fbfbf366112
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
sqlite3: f163dbbb7aa3339ad8fc622782c2d9d7b72f7e9c
tentap: 2a4256b6641a27d72d2b5fe2eb94cd1a203e4975
tentap: 2cf2e387dd284bf867010eb7d0f91618fb35b673
UMAppLoader: 5df85360d65cabaef544be5424ac64672e648482
Yoga: fb61b2337c7688c81a137e5560b3cbb515289f91

PODFILE CHECKSUM: 0cb7a78e5777e69c86c1bf4bb5135fd660376dbe

COCOAPODS: 1.16.2
COCOAPODS: 1.15.2
2 changes: 1 addition & 1 deletion apps/tlon-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@dev-plugins/react-query": "^0.0.6",
"@google-cloud/recaptcha-enterprise-react-native": "^18.3.0",
"@gorhom/bottom-sheet": "^4.5.1",
"@op-engineering/op-sqlite": "5.0.5",
"@op-engineering/op-sqlite": "11.2.4",
"@react-native-async-storage/async-storage": "1.21.0",
"@react-native-clipboard/clipboard": "^1.14.0",
"@react-native-community/netinfo": "11.1.0",
Expand Down
13 changes: 8 additions & 5 deletions packages/app/features/top/ChannelScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ export default function ChannelScreen(props: Props) {
const channelIsPending = !channel || channel.isPendingChannel;
useFocusEffect(
useCallback(() => {
if (!channelIsPending) {
store.syncChannelThreadUnreads(channelId, {
priority: store.SyncPriority.High,
});
}
// Mark the channel as visited when we unfocus/leave this screen
() => {
if (!channelIsPending) {
Expand Down Expand Up @@ -101,6 +96,14 @@ export default function ChannelScreen(props: Props) {
}, [groupId, channelId])
);

useEffect(() => {
if (!channelIsPending) {
store.syncChannelThreadUnreads(channelId, {
priority: store.SyncPriority.High,
});
}
}, [channelIsPending, channelId]);

const [channelNavOpen, setChannelNavOpen] = React.useState(false);
const [inviteSheetGroup, setInviteSheetGroup] =
React.useState<db.Group | null>();
Expand Down
77 changes: 77 additions & 0 deletions packages/app/lib/baseDb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { createDevLogger } from '@tloncorp/shared';
import { handleChange } from '@tloncorp/shared/db';
import { sql } from 'drizzle-orm';
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { useEffect, useMemo, useState } from 'react';

export const enableLogger = false;
export const logger = createDevLogger('db', enableLogger);

export const changeLogTable = sqliteTable('__change_log', {
id: integer('id').primaryKey(),
table_name: text('table_name').notNull(),
operation: text('operation').notNull(),
row_id: integer('row_id'),
row_data: text('row_data'),
timestamp: integer('timestamp').default(sql`(strftime('%s', 'now'))`),
});

export function useMigrations(db: BaseDb) {
const [hasSucceeded, setHasSucceeded] = useState(false);
const [error, setError] = useState(null);

useEffect(() => {
logger.log('running migrations');
const startTime = Date.now();
db.runMigrations()
.then(() => setHasSucceeded(true))
.catch((e) => {
logger.log('failed to migrate database', e);
setError(e);
})
.finally(() =>
logger.log('migrations complete in', Date.now() - startTime + 'ms')
);
}, [db]);

return useMemo(
() => ({
success: hasSucceeded,
error: error,
}),
[hasSucceeded, error]
);
}

export abstract class BaseDb {
protected client: any = null;
protected isPolling = false;

abstract setupDb(): Promise<void>;
abstract purgeDb(): Promise<void>;
abstract getDbPath(): Promise<string | undefined>;
abstract runMigrations(): Promise<void>;

protected async processChanges() {
if (!this.client) return;

try {
const changes = await this.client.select().from(changeLogTable).all();
for (const change of changes) {
handleChange({
table: change.table_name,
operation: change.operation as 'INSERT' | 'UPDATE' | 'DELETE',
row: JSON.parse(change.row_data ?? ''),
});
}
await this.client.delete(changeLogTable).run();
} catch (e) {
logger.error('failed to process changes:', e);
}
}

async resetDb() {
await this.purgeDb();
await this.runMigrations();
}
}
Loading

0 comments on commit 9036269

Please sign in to comment.