From 2962e682e48ffc4537d7ce907f894fe67eba9151 Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Wed, 6 Nov 2024 17:47:45 +0700 Subject: [PATCH] implement space hierarchy cache --- lib/src/client.dart | 34 +++++++++++++++++++ lib/src/database/database_api.dart | 7 ++++ .../database/hive_collections_database.dart | 32 +++++++++++++++++ lib/src/database/hive_database.dart | 29 ++++++++++++++++ lib/src/database/matrix_sdk_database.dart | 34 +++++++++++++++++++ 5 files changed, 136 insertions(+) diff --git a/lib/src/client.dart b/lib/src/client.dart index 2522e5536..1b4bcb4f8 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -2194,6 +2194,22 @@ class Client extends MatrixApi { EventUpdateType.accountData, ); } + + // invalidate _getSpaceHierarchyResponseCache + if (room.spaceParents.isNotEmpty) { + for (final space in room.spaceParents) { + final String? spaceId = space.roomId; + if (spaceId == null) continue; + if (spaceId == room.id) continue; + final Room? spaceAsRoom = getRoomById(spaceId); + if (spaceAsRoom == null) continue; + if (spaceAsRoom.spaceChildren + .map((child) => child.roomId) + .contains(room.id)) { + await database?.removeSpaceHierarchy(spaceId); + } + } + } } if (syncRoomUpdate is LeftRoomUpdate) { @@ -3323,6 +3339,24 @@ class Client extends MatrixApi { waitUntilLoadCompletedLoaded: false, ); } + + @override + Future getSpaceHierarchy(String roomId, + {bool? suggestedOnly, int? limit, int? maxDepth, String? from}) async { + final cachedResponse = await database?.getSpaceHierarchy(roomId); + if (cachedResponse == null) { + final response = await super.getSpaceHierarchy( + roomId, + suggestedOnly: suggestedOnly, + limit: limit, + maxDepth: maxDepth, + from: from, + ); + await database?.storeSpaceHierarchy(roomId, response); + return response; + } + return cachedResponse; + } } class SdkError { diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index 6f74ca88e..0915b26b6 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -327,6 +327,13 @@ abstract class DatabaseApi { Future getPresence(String userId); + Future getSpaceHierarchy(String spaceId); + + Future storeSpaceHierarchy( + String spaceId, GetSpaceHierarchyResponse hierarchy); + + Future removeSpaceHierarchy(String spaceId); + /// Deletes the whole database. The database needs to be created again after /// this. Used for migration only. Future delete(); diff --git a/lib/src/database/hive_collections_database.dart b/lib/src/database/hive_collections_database.dart index aeb225904..70bec6ea9 100644 --- a/lib/src/database/hive_collections_database.dart +++ b/lib/src/database/hive_collections_database.dart @@ -89,6 +89,8 @@ class HiveCollectionsDatabase extends DatabaseApi { late CollectionBox _seenDeviceKeysBox; + late CollectionBox _spacesHierarchyBox; + String get _clientBoxName => 'box_client'; String get _accountDataBoxName => 'box_account_data'; @@ -127,6 +129,8 @@ class HiveCollectionsDatabase extends DatabaseApi { String get _seenDeviceKeysBoxName => 'box_seen_device_keys'; + static const String _spacesHierarchyBoxName = 'box_spaces_hierarchy'; + HiveCollectionsDatabase( this.name, this.path, { @@ -160,6 +164,7 @@ class HiveCollectionsDatabase extends DatabaseApi { _eventsBoxName, _seenDeviceIdsBoxName, _seenDeviceKeysBoxName, + _spacesHierarchyBoxName, }, key: key, path: path, @@ -226,6 +231,9 @@ class HiveCollectionsDatabase extends DatabaseApi { _seenDeviceKeysBox = await _collection.openBox( _seenDeviceKeysBoxName, ); + _spacesHierarchyBox = await _collection.openBox( + _spacesHierarchyBoxName, + ); // Check version and check if we need a migration final currentVersion = int.tryParse(await _clientBox.get('version') ?? ''); @@ -280,6 +288,7 @@ class HiveCollectionsDatabase extends DatabaseApi { await _outboundGroupSessionsBox.clear(); await _presencesBox.clear(); await _clientBox.delete('prev_batch'); + await _spacesHierarchyBox.clear(); }); @override @@ -326,6 +335,11 @@ class HiveCollectionsDatabase extends DatabaseApi { if (multiKey.parts.first != roomId) continue; await _roomAccountDataBox.delete(key); } + final spaceHierarchyKeys = await _spacesHierarchyBox.getAllKeys(); + for (final key in spaceHierarchyKeys) { + if (key != roomId) continue; + await _spacesHierarchyBox.delete(key); + } await _roomsBox.delete(roomId); }); @@ -1517,6 +1531,7 @@ class HiveCollectionsDatabase extends DatabaseApi { _eventsBoxName: await _eventsBox.getAllValues(), _seenDeviceIdsBoxName: await _seenDeviceIdsBox.getAllValues(), _seenDeviceKeysBoxName: await _seenDeviceKeysBox.getAllValues(), + _spacesHierarchyBoxName: await _spacesHierarchyBox.getAllValues(), }; final json = jsonEncode(dataMap); await clear(); @@ -1588,6 +1603,9 @@ class HiveCollectionsDatabase extends DatabaseApi { for (final key in json[_seenDeviceKeysBoxName]!.keys) { await _seenDeviceKeysBox.put(key, json[_seenDeviceKeysBoxName]![key]); } + for (final key in json[_spacesHierarchyBoxName]!.keys) { + await _spacesHierarchyBox.put(key, json[_spacesHierarchyBoxName]![key]); + } return true; } catch (e, s) { Logs().e('Database import error: ', e, s); @@ -1595,6 +1613,20 @@ class HiveCollectionsDatabase extends DatabaseApi { } } + @override + Future getSpaceHierarchy(String spaceId) async { + return await _spacesHierarchyBox.get(spaceId); + } + + @override + Future storeSpaceHierarchy( + String spaceId, GetSpaceHierarchyResponse hierarchy) => + _spacesHierarchyBox.put(spaceId, hierarchy); + + @override + Future removeSpaceHierarchy(String spaceId) => + _spacesHierarchyBox.delete(spaceId); + @override Future delete() => _collection.deleteFromDisk(); } diff --git a/lib/src/database/hive_database.dart b/lib/src/database/hive_database.dart index ddf4f92ef..c025d73b3 100644 --- a/lib/src/database/hive_database.dart +++ b/lib/src/database/hive_database.dart @@ -84,6 +84,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { late LazyBox _seenDeviceKeysBox; + late LazyBox _spacesHierarchyBox; + String get _clientBoxName => '$name.box.client'; String get _accountDataBoxName => '$name.box.account_data'; @@ -124,6 +126,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { String get _seenDeviceKeysBoxName => '$name.box.seen_device_keys'; + static const String _spacesHierarchyBoxName = 'box_spaces_hierarchy'; + final HiveCipher? encryptionCipher; FamedlySdkHiveDatabase(this.name, {this.encryptionCipher}); @@ -152,6 +156,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { action(_eventsBox), action(_seenDeviceIdsBox), action(_seenDeviceKeysBox), + action(_spacesHierarchyBox) ]); Future open() async { @@ -232,6 +237,11 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { encryptionCipher: encryptionCipher, ); + _spacesHierarchyBox = await Hive.openLazyBox( + _spacesHierarchyBoxName, + encryptionCipher: encryptionCipher, + ); + // Check version and check if we need a migration final currentVersion = (await _clientBox.get('version') as int?); if (currentVersion == null) { @@ -295,6 +305,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { await _outboundGroupSessionsBox.deleteAll(_outboundGroupSessionsBox.keys); await _presencesBox.deleteAll(_presencesBox.keys); await _clientBox.delete('prev_batch'); + await _spacesHierarchyBox.clear(); } @override @@ -339,6 +350,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { if (multiKey.parts.first != roomId) continue; await _roomAccountDataBox.delete(key); } + for (final key in _spacesHierarchyBox.keys) { + if (key != roomId) continue; + await _spacesHierarchyBox.delete(key); + } await _roomsBox.delete(roomId.toHiveKey); } @@ -1401,6 +1416,20 @@ class FamedlySdkHiveDatabase extends DatabaseApi with ZoneTransactionMixin { throw UnimplementedError(); } + @override + Future getSpaceHierarchy(String spaceId) async { + return await _spacesHierarchyBox.get(spaceId); + } + + @override + Future storeSpaceHierarchy( + String spaceId, GetSpaceHierarchyResponse hierarchy) => + _spacesHierarchyBox.put(spaceId, hierarchy); + + @override + Future removeSpaceHierarchy(String spaceId) => + _spacesHierarchyBox.delete(spaceId); + @override Future delete() => Hive.deleteFromDisk(); } diff --git a/lib/src/database/matrix_sdk_database.dart b/lib/src/database/matrix_sdk_database.dart index c77b5a956..8458bd80a 100644 --- a/lib/src/database/matrix_sdk_database.dart +++ b/lib/src/database/matrix_sdk_database.dart @@ -103,6 +103,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { late Box _seenDeviceKeysBox; + late Box _spacesHierarchyBox; + @override final int maxFileSize; @@ -159,6 +161,8 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { static const String _seenDeviceKeysBoxName = 'box_seen_device_keys'; + static const String _spacesHierarchyBoxName = 'box_spaces_hierarchy'; + Database? database; /// Custom IdbFactory used to create the indexedDB. On IO platforms it would @@ -214,6 +218,7 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { _eventsBoxName, _seenDeviceIdsBoxName, _seenDeviceKeysBoxName, + _spacesHierarchyBoxName, }, sqfliteDatabase: database, sqfliteFactory: sqfliteFactory, @@ -283,6 +288,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { _seenDeviceKeysBox = _collection.openBox( _seenDeviceKeysBoxName, ); + _spacesHierarchyBox = _collection.openBox( + _spacesHierarchyBoxName, + ); // Check version and check if we need a migration final currentVersion = int.tryParse(await _clientBox.get('version') ?? ''); @@ -341,6 +349,7 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { await _outboundGroupSessionsBox.clear(); await _presencesBox.clear(); await _clientBox.delete('prev_batch'); + await _spacesHierarchyBox.clear(); }); @override @@ -389,6 +398,11 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { if (multiKey.parts.first != roomId) continue; await _roomAccountDataBox.delete(key); } + final spaceHierarchyKeys = await _spacesHierarchyBox.getAllKeys(); + for (final key in spaceHierarchyKeys) { + if (key != roomId) continue; + await _spacesHierarchyBox.delete(key); + } await _roomsBox.delete(roomId); } @@ -1477,6 +1491,7 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { _eventsBoxName: await _eventsBox.getAllValues(), _seenDeviceIdsBoxName: await _seenDeviceIdsBox.getAllValues(), _seenDeviceKeysBoxName: await _seenDeviceKeysBox.getAllValues(), + _spacesHierarchyBoxName: await _spacesHierarchyBox.getAllValues(), }; final json = jsonEncode(dataMap); await clear(); @@ -1557,6 +1572,9 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { for (final key in json[_seenDeviceKeysBoxName]!.keys) { await _seenDeviceKeysBox.put(key, json[_seenDeviceKeysBoxName]![key]); } + for (final key in json[_spacesHierarchyBoxName]!.keys) { + await _spacesHierarchyBox.put(key, json[_spacesHierarchyBoxName]![key]); + } return true; } catch (e, s) { Logs().e('Database import error: ', e, s); @@ -1613,6 +1631,22 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage { return CachedPresence.fromJson(copyMap(rawPresence)); } + @override + Future getSpaceHierarchy(String spaceId) async { + final raw_space_hierarchy = await _spacesHierarchyBox.get(spaceId); + if (raw_space_hierarchy == null) return null; + return GetSpaceHierarchyResponse.fromJson(copyMap(raw_space_hierarchy)); + } + + @override + Future storeSpaceHierarchy( + String spaceId, GetSpaceHierarchyResponse hierarchy) => + _spacesHierarchyBox.put(spaceId, hierarchy.toJson()); + + @override + Future removeSpaceHierarchy(String spaceId) => + _spacesHierarchyBox.delete(spaceId); + @override Future delete() => BoxCollection.delete( name,