fix: clear cache when clearing DB in MatrixSdkDatabase

This commit is contained in:
Karthikeyan S 2024-12-20 16:46:59 +05:30
parent c87e2eeff2
commit 83003c86c7
No known key found for this signature in database
GPG Key ID: 28BA6AEE539ECE2E
3 changed files with 81 additions and 55 deletions

View File

@ -100,27 +100,25 @@ class BoxCollection with ZoneTransactionMixin {
class Box<V> {
final String name;
final BoxCollection boxCollection;
final Map<String, V?> _cache = {};
final Map<String, V?> _quickAccessCache = {};
/// _cachedKeys is only used to make sure that if you fetch all keys from a
/// _quickAccessCachedKeys is only used to make sure that if you fetch all keys from a
/// box, you do not need to have an expensive read operation twice. There is
/// no other usage for this at the moment. So the cache is never partial.
/// Once the keys are cached, they need to be updated when changed in put and
/// delete* so that the cache does not become outdated.
Set<String>? _cachedKeys;
bool get _keysCached => _cachedKeys != null;
Set<String>? _quickAccessCachedKeys;
Box(this.name, this.boxCollection);
Future<List<String>> getAllKeys([Transaction? txn]) async {
if (_keysCached) return _cachedKeys!.toList();
if (_quickAccessCachedKeys != null) return _quickAccessCachedKeys!.toList();
txn ??= boxCollection._db.transaction(name, 'readonly');
final store = txn.objectStore(name);
final request = store.getAllKeys(null);
await request.onSuccess.first;
final keys = request.result.cast<String>();
_cachedKeys = keys.toSet();
_quickAccessCachedKeys = keys.toSet();
return keys;
}
@ -136,16 +134,16 @@ class Box<V> {
}
Future<V?> get(String key, [Transaction? txn]) async {
if (_cache.containsKey(key)) return _cache[key];
if (_quickAccessCache.containsKey(key)) return _quickAccessCache[key];
txn ??= boxCollection._db.transaction(name, 'readonly');
final store = txn.objectStore(name);
_cache[key] = await store.getObject(key).then(_fromValue);
return _cache[key];
_quickAccessCache[key] = await store.getObject(key).then(_fromValue);
return _quickAccessCache[key];
}
Future<List<V?>> getAll(List<String> keys, [Transaction? txn]) async {
if (keys.every((key) => _cache.containsKey(key))) {
return keys.map((key) => _cache[key]).toList();
if (keys.every((key) => _quickAccessCache.containsKey(key))) {
return keys.map((key) => _quickAccessCache[key]).toList();
}
txn ??= boxCollection._db.transaction(name, 'readonly');
final store = txn.objectStore(name);
@ -153,7 +151,7 @@ class Box<V> {
keys.map((key) => store.getObject(key).then(_fromValue)),
);
for (var i = 0; i < keys.length; i++) {
_cache[keys[i]] = list[i];
_quickAccessCache[keys[i]] = list[i];
}
return list;
}
@ -161,24 +159,24 @@ class Box<V> {
Future<void> put(String key, V val, [Transaction? txn]) async {
if (boxCollection._txnCache != null) {
boxCollection._txnCache!.add((txn) => put(key, val, txn));
_cache[key] = val;
_cachedKeys?.add(key);
_quickAccessCache[key] = val;
_quickAccessCachedKeys?.add(key);
return;
}
txn ??= boxCollection._db.transaction(name, 'readwrite');
final store = txn.objectStore(name);
await store.put(val as Object, key);
_cache[key] = val;
_cachedKeys?.add(key);
_quickAccessCache[key] = val;
_quickAccessCachedKeys?.add(key);
return;
}
Future<void> delete(String key, [Transaction? txn]) async {
if (boxCollection._txnCache != null) {
boxCollection._txnCache!.add((txn) => delete(key, txn));
_cache[key] = null;
_cachedKeys?.remove(key);
_quickAccessCache[key] = null;
_quickAccessCachedKeys?.remove(key);
return;
}
@ -188,8 +186,8 @@ class Box<V> {
// Set to null instead remove() so that inside of transactions null is
// returned.
_cache[key] = null;
_cachedKeys?.remove(key);
_quickAccessCache[key] = null;
_quickAccessCachedKeys?.remove(key);
return;
}
@ -197,9 +195,9 @@ class Box<V> {
if (boxCollection._txnCache != null) {
boxCollection._txnCache!.add((txn) => deleteAll(keys, txn));
for (final key in keys) {
_cache[key] = null;
_quickAccessCache[key] = null;
}
_cachedKeys?.removeAll(keys);
_quickAccessCachedKeys?.removeAll(keys);
return;
}
@ -207,26 +205,27 @@ class Box<V> {
final store = txn.objectStore(name);
for (final key in keys) {
await store.delete(key);
_cache[key] = null;
_cachedKeys?.remove(key);
_quickAccessCache[key] = null;
_quickAccessCachedKeys?.remove(key);
}
return;
}
void clearQuickAccessCache() {
_quickAccessCache.clear();
_quickAccessCachedKeys = null;
}
Future<void> clear([Transaction? txn]) async {
if (boxCollection._txnCache != null) {
boxCollection._txnCache!.add((txn) => clear(txn));
_cache.clear();
_cachedKeys = null;
return;
} else {
txn ??= boxCollection._db.transaction(name, 'readwrite');
final store = txn.objectStore(name);
await store.clear();
}
txn ??= boxCollection._db.transaction(name, 'readwrite');
final store = txn.objectStore(name);
await store.clear();
_cache.clear();
_cachedKeys = null;
return;
clearQuickAccessCache();
}
V? _fromValue(Object? value) {

View File

@ -338,7 +338,32 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage {
}
@override
Future<void> clear() => _collection.clear();
Future<void> clear() async {
_clientBox.clearQuickAccessCache();
_accountDataBox.clearQuickAccessCache();
_roomsBox.clearQuickAccessCache();
_preloadRoomStateBox.clearQuickAccessCache();
_nonPreloadRoomStateBox.clearQuickAccessCache();
_roomMembersBox.clearQuickAccessCache();
_toDeviceQueueBox.clearQuickAccessCache();
_roomAccountDataBox.clearQuickAccessCache();
_inboundGroupSessionsBox.clearQuickAccessCache();
_inboundGroupSessionsUploadQueueBox.clearQuickAccessCache();
_outboundGroupSessionsBox.clearQuickAccessCache();
_olmSessionsBox.clearQuickAccessCache();
_userDeviceKeysBox.clearQuickAccessCache();
_userDeviceKeysOutdatedBox.clearQuickAccessCache();
_userCrossSigningKeysBox.clearQuickAccessCache();
_ssssCacheBox.clearQuickAccessCache();
_presencesBox.clearQuickAccessCache();
_timelineFragmentsBox.clearQuickAccessCache();
_eventsBox.clearQuickAccessCache();
_seenDeviceIdsBox.clearQuickAccessCache();
_seenDeviceKeysBox.clearQuickAccessCache();
_userProfilesBox.clearQuickAccessCache();
await _collection.clear();
}
@override
Future<void> clearCache() => transaction(() async {

View File

@ -81,15 +81,14 @@ class BoxCollection with ZoneTransactionMixin {
class Box<V> {
final String name;
final BoxCollection boxCollection;
final Map<String, V?> _cache = {};
final Map<String, V?> _quickAccessCache = {};
/// _cachedKeys is only used to make sure that if you fetch all keys from a
/// _quickAccessCachedKeys is only used to make sure that if you fetch all keys from a
/// box, you do not need to have an expensive read operation twice. There is
/// no other usage for this at the moment. So the cache is never partial.
/// Once the keys are cached, they need to be updated when changed in put and
/// delete* so that the cache does not become outdated.
Set<String>? _cachedKeys;
bool get _keysCached => _cachedKeys != null;
Set<String>? _quickAccessCachedKeys;
static const Set<Type> allowedValueTypes = {
List<dynamic>,
@ -148,14 +147,14 @@ class Box<V> {
}
Future<List<String>> getAllKeys([Transaction? txn]) async {
if (_keysCached) return _cachedKeys!.toList();
if (_quickAccessCachedKeys != null) return _quickAccessCachedKeys!.toList();
final executor = txn ?? boxCollection._db;
final result = await executor.query(name, columns: ['k']);
final keys = result.map((row) => row['k'] as String).toList();
_cachedKeys = keys.toSet();
_quickAccessCachedKeys = keys.toSet();
return keys;
}
@ -174,7 +173,7 @@ class Box<V> {
}
Future<V?> get(String key, [Transaction? txn]) async {
if (_cache.containsKey(key)) return _cache[key];
if (_quickAccessCache.containsKey(key)) return _quickAccessCache[key];
final executor = txn ?? boxCollection._db;
@ -186,13 +185,13 @@ class Box<V> {
);
final value = result.isEmpty ? null : _fromString(result.single['v']);
_cache[key] = value;
_quickAccessCache[key] = value;
return value;
}
Future<List<V?>> getAll(List<String> keys, [Transaction? txn]) async {
if (!keys.any((key) => !_cache.containsKey(key))) {
return keys.map((key) => _cache[key]).toList();
if (!keys.any((key) => !_quickAccessCache.containsKey(key))) {
return keys.map((key) => _quickAccessCache[key]).toList();
}
// The SQL operation might fail with more than 1000 keys. We define some
@ -224,7 +223,7 @@ class Box<V> {
// `resultMap.values`.
list.addAll(keys.map((key) => resultMap[key]));
_cache.addAll(resultMap);
_quickAccessCache.addAll(resultMap);
return list;
}
@ -250,8 +249,8 @@ class Box<V> {
);
}
_cache[key] = val;
_cachedKeys?.add(key);
_quickAccessCache[key] = val;
_quickAccessCachedKeys?.add(key);
return;
}
@ -266,8 +265,8 @@ class Box<V> {
// Set to null instead remove() so that inside of transactions null is
// returned.
_cache[key] = null;
_cachedKeys?.remove(key);
_quickAccessCache[key] = null;
_quickAccessCachedKeys?.remove(key);
return;
}
@ -290,12 +289,17 @@ class Box<V> {
}
for (final key in keys) {
_cache[key] = null;
_cachedKeys?.removeAll(keys);
_quickAccessCache[key] = null;
_quickAccessCachedKeys?.removeAll(keys);
}
return;
}
void clearQuickAccessCache() {
_quickAccessCache.clear();
_quickAccessCachedKeys = null;
}
Future<void> clear([Batch? txn]) async {
txn ??= boxCollection._activeBatch;
@ -305,8 +309,6 @@ class Box<V> {
txn.delete(name);
}
_cache.clear();
_cachedKeys = null;
return;
clearQuickAccessCache();
}
}