separate firo caches and add versioning

This commit is contained in:
julian 2024-06-05 13:38:20 -06:00
parent 23db925e86
commit 2c07f2c13b
5 changed files with 142 additions and 43 deletions

View file

@ -15,8 +15,8 @@ import '../../utilities/stack_file_system.dart';
part 'firo_cache_coordinator.dart'; part 'firo_cache_coordinator.dart';
part 'firo_cache_reader.dart'; part 'firo_cache_reader.dart';
part 'firo_cache_writer.dart';
part 'firo_cache_worker.dart'; part 'firo_cache_worker.dart';
part 'firo_cache_writer.dart';
/// Temporary debugging log function for this file /// Temporary debugging log function for this file
void _debugLog(Object? object) { void _debugLog(Object? object) {
@ -29,44 +29,73 @@ void _debugLog(Object? object) {
} }
abstract class _FiroCache { abstract class _FiroCache {
static const String sqliteDbFileName = "firo_ex_cache.sqlite3"; static const int _setCacheVersion = 1;
static const int _tagsCacheVersion = 1;
static const String sparkSetCacheFileName =
"spark_set_v$_setCacheVersion.sqlite3";
static const String sparkUsedTagsCacheFileName =
"spark_tags_v$_tagsCacheVersion.sqlite3";
static Database? _db; static Database? _setCacheDB;
static Database get db { static Database? _usedTagsCacheDB;
if (_db == null) { static Database get setCacheDB {
if (_setCacheDB == null) {
throw Exception( throw Exception(
"FiroCache.init() must be called before accessing FiroCache.db!", "FiroCache.init() must be called before accessing FiroCache.db!",
); );
} }
return _db!; return _setCacheDB!;
}
static Database get usedTagsCacheDB {
if (_usedTagsCacheDB == null) {
throw Exception(
"FiroCache.init() must be called before accessing FiroCache.db!",
);
}
return _usedTagsCacheDB!;
} }
static Future<void>? _initFuture; static Future<void>? _initFuture;
static Future<void> init() => _initFuture ??= _init(); static Future<void> init() => _initFuture ??= _init();
static Future<void> _init() async { static Future<void> _init() async {
final sqliteDir = await StackFileSystem.applicationSQLiteDirectory(); final sqliteDir =
await StackFileSystem.applicationFiroCacheSQLiteDirectory();
final file = File("${sqliteDir.path}/$sqliteDbFileName"); final sparkSetCacheFile = File("${sqliteDir.path}/$sparkSetCacheFileName");
final sparkUsedTagsCacheFile =
File("${sqliteDir.path}/$sparkUsedTagsCacheFileName");
final exists = await file.exists(); if (!(await sparkSetCacheFile.exists())) {
if (!exists) { await _createSparkSetCacheDb(sparkSetCacheFile.path);
await _createDb(file.path); }
if (!(await sparkUsedTagsCacheFile.exists())) {
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
} }
_db = sqlite3.open( _setCacheDB = sqlite3.open(
file.path, sparkSetCacheFile.path,
mode: OpenMode.readWrite,
);
_usedTagsCacheDB = sqlite3.open(
sparkUsedTagsCacheFile.path,
mode: OpenMode.readWrite, mode: OpenMode.readWrite,
); );
} }
static Future<void> _deleteAllCache() async { static Future<void> _deleteAllCache() async {
final start = DateTime.now(); final start = DateTime.now();
db.execute( setCacheDB.execute(
""" """
DELETE FROM SparkSet; DELETE FROM SparkSet;
DELETE FROM SparkCoin; DELETE FROM SparkCoin;
DELETE FROM SparkSetCoins; DELETE FROM SparkSetCoins;
VACUUM;
""",
);
usedTagsCacheDB.execute(
"""
DELETE FROM SparkUsedCoinTags; DELETE FROM SparkUsedCoinTags;
VACUUM; VACUUM;
""", """,
@ -77,7 +106,7 @@ abstract class _FiroCache {
); );
} }
static Future<void> _createDb(String file) async { static Future<void> _createSparkSetCacheDb(String file) async {
final db = sqlite3.open( final db = sqlite3.open(
file, file,
mode: OpenMode.readWriteCreate, mode: OpenMode.readWriteCreate,
@ -109,7 +138,20 @@ abstract class _FiroCache {
FOREIGN KEY (setId) REFERENCES SparkSet(id), FOREIGN KEY (setId) REFERENCES SparkSet(id),
FOREIGN KEY (coinId) REFERENCES SparkCoin(id) FOREIGN KEY (coinId) REFERENCES SparkCoin(id)
); );
""",
);
db.dispose();
}
static Future<void> _createSparkUsedTagsCacheDb(String file) async {
final db = sqlite3.open(
file,
mode: OpenMode.readWriteCreate,
);
db.execute(
"""
CREATE TABLE SparkUsedCoinTags ( CREATE TABLE SparkUsedCoinTags (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
tag TEXT NOT NULL UNIQUE tag TEXT NOT NULL UNIQUE

View file

@ -20,14 +20,18 @@ abstract class FiroCacheCoordinator {
} }
static Future<String> getSparkCacheSize() async { static Future<String> getSparkCacheSize() async {
final dir = await StackFileSystem.applicationSQLiteDirectory(); final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
final cacheFile = File("${dir.path}/${_FiroCache.sqliteDbFileName}"); final setCacheFile = File(
final int bytes; "${dir.path}/${_FiroCache.sparkSetCacheFileName}",
if (await cacheFile.exists()) { );
bytes = await cacheFile.length(); final usedTagsCacheFile = File(
} else { "${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}",
bytes = 0; );
} final int bytes =
((await setCacheFile.exists()) ? await setCacheFile.length() : 0) +
((await usedTagsCacheFile.exists())
? await usedTagsCacheFile.length()
: 0);
if (bytes < 1024) { if (bytes < 1024) {
return '$bytes B'; return '$bytes B';
@ -88,7 +92,7 @@ abstract class FiroCacheCoordinator {
static Future<Set<String>> getUsedCoinTags(int startNumber) async { static Future<Set<String>> getUsedCoinTags(int startNumber) async {
final result = await _Reader._getSparkUsedCoinTags( final result = await _Reader._getSparkUsedCoinTags(
startNumber, startNumber,
db: _FiroCache.db, db: _FiroCache.usedTagsCacheDB,
); );
return result.map((e) => e["tag"] as String).toSet(); return result.map((e) => e["tag"] as String).toSet();
} }
@ -99,7 +103,7 @@ abstract class FiroCacheCoordinator {
/// this table in practice. /// this table in practice.
static Future<int> getUsedCoinTagsLastAddedRowId() async { static Future<int> getUsedCoinTagsLastAddedRowId() async {
final result = await _Reader._getUsedCoinTagsLastAddedRowId( final result = await _Reader._getUsedCoinTagsLastAddedRowId(
db: _FiroCache.db, db: _FiroCache.usedTagsCacheDB,
); );
if (result.isEmpty) { if (result.isEmpty) {
return 0; return 0;
@ -112,7 +116,7 @@ abstract class FiroCacheCoordinator {
) async { ) async {
return await _Reader._checkTagIsUsed( return await _Reader._checkTagIsUsed(
tag, tag,
db: _FiroCache.db, db: _FiroCache.usedTagsCacheDB,
); );
} }
@ -122,7 +126,7 @@ abstract class FiroCacheCoordinator {
}) async { }) async {
return await _Reader._getSetCoinsForGroupId( return await _Reader._getSetCoinsForGroupId(
groupId, groupId,
db: _FiroCache.db, db: _FiroCache.setCacheDB,
newerThanTimeStamp: newerThanTimeStamp, newerThanTimeStamp: newerThanTimeStamp,
); );
} }
@ -137,7 +141,7 @@ abstract class FiroCacheCoordinator {
) async { ) async {
final result = await _Reader._getLatestSetInfoForGroupId( final result = await _Reader._getLatestSetInfoForGroupId(
groupId, groupId,
db: _FiroCache.db, db: _FiroCache.setCacheDB,
); );
if (result.isEmpty) { if (result.isEmpty) {
@ -156,7 +160,7 @@ abstract class FiroCacheCoordinator {
) async { ) async {
return await _Reader._checkSetInfoForGroupIdExists( return await _Reader._checkSetInfoForGroupIdExists(
groupId, groupId,
db: _FiroCache.db, db: _FiroCache.setCacheDB,
); );
} }
} }

View file

@ -26,8 +26,10 @@ class _FiroCacheWorker {
} }
static Future<_FiroCacheWorker> spawn() async { static Future<_FiroCacheWorker> spawn() async {
final sqliteDir = await StackFileSystem.applicationSQLiteDirectory(); final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
final dbFilePath = "${sqliteDir.path}/${_FiroCache.sqliteDbFileName}"; final setCacheFilePath = "${dir.path}/${_FiroCache.sparkSetCacheFileName}";
final usedTagsCacheFilePath =
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}";
final initPort = RawReceivePort(); final initPort = RawReceivePort();
final connection = Completer<(ReceivePort, SendPort)>.sync(); final connection = Completer<(ReceivePort, SendPort)>.sync();
@ -45,7 +47,7 @@ class _FiroCacheWorker {
try { try {
await Isolate.spawn( await Isolate.spawn(
_startWorkerIsolate, _startWorkerIsolate,
(initPort.sendPort, dbFilePath), (initPort.sendPort, setCacheFilePath, usedTagsCacheFilePath),
); );
} catch (_) { } catch (_) {
initPort.close(); initPort.close();
@ -75,7 +77,8 @@ class _FiroCacheWorker {
static void _handleCommandsToIsolate( static void _handleCommandsToIsolate(
ReceivePort receivePort, ReceivePort receivePort,
SendPort sendPort, SendPort sendPort,
Database db, Database setCacheDb,
Database usedTagsCacheDb,
Mutex mutex, Mutex mutex,
) { ) {
receivePort.listen((message) { receivePort.listen((message) {
@ -87,11 +90,18 @@ class _FiroCacheWorker {
switch (task.func) { switch (task.func) {
case FCFuncName._updateSparkAnonSetCoinsWith: case FCFuncName._updateSparkAnonSetCoinsWith:
final data = task.data as (int, Map<String, dynamic>); final data = task.data as (int, Map<String, dynamic>);
result = _updateSparkAnonSetCoinsWith(db, data.$2, data.$1); result = _updateSparkAnonSetCoinsWith(
setCacheDb,
data.$2,
data.$1,
);
break; break;
case FCFuncName._updateSparkUsedTagsWith: case FCFuncName._updateSparkUsedTagsWith:
result = _updateSparkUsedTagsWith(db, task.data as List<String>); result = _updateSparkUsedTagsWith(
usedTagsCacheDb,
task.data as List<String>,
);
break; break;
} }
@ -107,14 +117,24 @@ class _FiroCacheWorker {
}); });
} }
static void _startWorkerIsolate((SendPort, String) args) { static void _startWorkerIsolate((SendPort, String, String) args) {
final receivePort = ReceivePort(); final receivePort = ReceivePort();
args.$1.send(receivePort.sendPort); args.$1.send(receivePort.sendPort);
final mutex = Mutex(); final mutex = Mutex();
final db = sqlite3.open( final setCacheDb = sqlite3.open(
args.$2, args.$2,
mode: OpenMode.readWrite, mode: OpenMode.readWrite,
); );
_handleCommandsToIsolate(receivePort, args.$1, db, mutex); final usedTagsCacheDb = sqlite3.open(
args.$3,
mode: OpenMode.readWrite,
);
_handleCommandsToIsolate(
receivePort,
args.$1,
setCacheDb,
usedTagsCacheDb,
mutex,
);
} }
} }

View file

@ -18,6 +18,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import '../../../db/sqlite/firo_cache.dart';
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
import '../../../models/isar/models/isar_models.dart'; import '../../../models/isar/models/isar_models.dart';
import '../../../pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart'; import '../../../pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart';
@ -282,6 +283,24 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
), ),
], ],
), ),
if (wallet.isarTransactionVersion == 2 &&
wallet is FiroWallet)
Row(
children: [
const Text(
"sparkCache: ",
),
const SizedBox(
width: 2,
),
FutureBuilder(
future: FiroCacheCoordinator.getSparkCacheSize(),
builder: (_, snapshot) => Text(
snapshot.data ?? "",
),
),
],
),
], ],
), ),
const Spacer(), const Spacer(),

View file

@ -91,10 +91,24 @@ abstract class StackFileSystem {
} }
} }
static Future<Directory> applicationSQLiteDirectory() async { // Not used in general now. See applicationFiroCacheSQLiteDirectory()
// static Future<Directory> applicationSQLiteDirectory() async {
// final root = await applicationRootDirectory();
// if (Util.isDesktop) {
// final dir = Directory("${root.path}/sqlite");
// if (!dir.existsSync()) {
// await dir.create();
// }
// return dir;
// } else {
// return root;
// }
// }
static Future<Directory> applicationTorDirectory() async {
final root = await applicationRootDirectory(); final root = await applicationRootDirectory();
if (Util.isDesktop) { if (Util.isDesktop) {
final dir = Directory("${root.path}/sqlite"); final dir = Directory("${root.path}/tor");
if (!dir.existsSync()) { if (!dir.existsSync()) {
await dir.create(); await dir.create();
} }
@ -104,10 +118,10 @@ abstract class StackFileSystem {
} }
} }
static Future<Directory> applicationTorDirectory() async { static Future<Directory> applicationFiroCacheSQLiteDirectory() async {
final root = await applicationRootDirectory(); final root = await applicationRootDirectory();
if (Util.isDesktop) { if (Util.isDesktop) {
final dir = Directory("${root.path}/tor"); final dir = Directory("${root.path}/sqlite/firo_cache");
if (!dir.existsSync()) { if (!dir.existsSync()) {
await dir.create(); await dir.create();
} }