Skip to content

Commit

Permalink
Added cache hits & misses retrieval
Browse files Browse the repository at this point in the history
Started connection of backend to frontend
  • Loading branch information
JaffaKetchup committed Dec 26, 2023
1 parent 389e40a commit f9f4009
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 67 deletions.
22 changes: 11 additions & 11 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,28 @@ environment:

dependencies:
auto_size_text: ^3.0.0
badges: ^3.0.2
badges: ^3.1.2
better_open_file: ^3.6.4
collection: ^1.17.1
collection: ^1.18.0
dart_earcut: ^1.0.1
file_picker: ^5.2.10
flutter:
sdk: flutter
flutter_foreground_task: ^6.0.0+1
flutter_map: ^6.0.0
flutter_foreground_task: ^6.1.2
flutter_map: ^6.1.0
flutter_map_animations: ^0.5.3
flutter_map_tile_caching:
flutter_map_tile_caching: ^9.0.0-dev.5
flutter_speed_dial: ^7.0.0
fmtc_plus_sharing: ^8.0.0
google_fonts: ^5.1.0
google_fonts: ^6.1.0
gpx: ^2.2.1
http: ^1.0.0
intl: ^0.18.0
http: ^1.1.2
intl: ^0.19.0
latlong2: ^0.9.0
osm_nominatim: ^3.0.0
path: ^1.8.3
provider: ^6.0.3
stream_transform: ^2.0.0
path: ^1.9.0
provider: ^6.1.1
stream_transform: ^2.1.0
validators: ^3.0.0
version: ^3.0.2

Expand Down
22 changes: 22 additions & 0 deletions lib/src/backend/impl_tools/no_sync.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,28 @@ mixin FMTCBackendNoSync implements FMTCBackendInternal {
@override
final supportsSyncGetStoreLength = false;

/// This synchronous method is unsupported by this implementation - use
/// [getStoreHits] instead
@override
int getStoreHitsSync({
required String storeName,
}) =>
throw SyncOperationUnsupported();

@override
final supportsSyncGetStoreHits = false;

/// This synchronous method is unsupported by this implementation - use
/// [getStoreMisses] instead
@override
int getStoreMissesSync({
required String storeName,
}) =>
throw SyncOperationUnsupported();

@override
final supportsSyncGetStoreMisses = false;

/// This synchronous method is unsupported by this implementation - use
/// [readTile] instead
@override
Expand Down
50 changes: 32 additions & 18 deletions lib/src/backend/impls/objectbox/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';

import 'package:async/async.dart';
import 'package:collection/collection.dart';
import 'package:meta/meta.dart' as meta;
import 'package:path_provider/path_provider.dart';
Expand Down Expand Up @@ -46,13 +45,10 @@ class _ObjectBoxBackendImpl
// for (final v in _WorkerKey.values) v: null,
//};

// TODO: Make cmds that need to be idempotent for performance, like
// delete oldest tile

// `deleteOldestTile`
int _numberOfOldestTilesToDelete = 0;
Timer? _deleteOldestTileTimer;
String? _storeNameOfCurrentDeleteOldestTimer;
// `deleteOldestTile` tracking & debouncing
int _dotLength = 0;
Timer? _dotDebouncer;
String? _dotStore;

Future<_WorkerRes> sendCmd(_WorkerCmd cmd) async {
expectInitialised;
Expand Down Expand Up @@ -187,6 +183,24 @@ class _ObjectBoxBackendImpl
))
.data!['length']! as int;

@override
Future<int> getStoreHits({
required String storeName,
}) async =>
(await sendCmd(
(key: _WorkerKey.getStoreHits, args: {'storeName': storeName}),
))
.data!['hits']! as int;

@override
Future<int> getStoreMisses({
required String storeName,
}) async =>
(await sendCmd(
(key: _WorkerKey.getStoreMisses, args: {'storeName': storeName}),
))
.data!['misses']! as int;

@override
Future<ObjectBoxTile?> readTile({
required String url,
Expand Down Expand Up @@ -226,11 +240,11 @@ class _ObjectBoxBackendImpl
key: _WorkerKey.removeOldestTile,
args: {
'storeName': storeName,
'number': _numberOfOldestTilesToDelete,
'number': _dotLength,
}
),
);
_numberOfOldestTilesToDelete = 0;
_dotLength = 0;
}

@override
Expand All @@ -240,26 +254,26 @@ class _ObjectBoxBackendImpl
// Attempts to avoid flooding worker with requests to delete oldest tile,
// and 'batches' them instead

if (_storeNameOfCurrentDeleteOldestTimer != storeName) {
if (_dotStore != storeName) {
// If the store has changed, failing to reset the batch/queue will mean
// tiles are removed from the wrong store
_storeNameOfCurrentDeleteOldestTimer = storeName;
if (_deleteOldestTileTimer != null && _deleteOldestTileTimer!.isActive) {
_deleteOldestTileTimer!.cancel();
_dotStore = storeName;
if (_dotDebouncer != null && _dotDebouncer!.isActive) {
_dotDebouncer!.cancel();
_sendRemoveOldestTileCmd(storeName);
}
}

if (_deleteOldestTileTimer != null && _deleteOldestTileTimer!.isActive) {
_deleteOldestTileTimer!.cancel();
_deleteOldestTileTimer = Timer(
if (_dotDebouncer != null && _dotDebouncer!.isActive) {
_dotDebouncer!.cancel();
_dotDebouncer = Timer(
const Duration(milliseconds: 500),
() => _sendRemoveOldestTileCmd(storeName),
);
return;
}

_deleteOldestTileTimer = Timer(
_dotDebouncer = Timer(
const Duration(seconds: 1),
() => _sendRemoveOldestTileCmd(storeName),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"entities": [
{
"id": "1:7419244569066266196",
"lastPropertyId": "4:3677248801338209880",
"lastPropertyId": "6:3446172587861422549",
"name": "ObjectBoxStore",
"properties": [
{
Expand All @@ -30,6 +30,16 @@
"id": "4:3677248801338209880",
"name": "numberOfBytes",
"type": 8
},
{
"id": "5:1702586868505261124",
"name": "hits",
"type": 6
},
{
"id": "6:3446172587861422549",
"name": "misses",
"type": 6
}
],
"relations": []
Expand Down
10 changes: 10 additions & 0 deletions lib/src/backend/impls/objectbox/models/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@ base class ObjectBoxStore extends BackendStore {
@Unique()
String name;

@override
int numberOfTiles;

@override
double numberOfBytes;

@override
int hits;

@override
int misses;

@Index()
@Backlink()
final tiles = ToMany<ObjectBoxTile>();
Expand All @@ -26,6 +34,8 @@ base class ObjectBoxStore extends BackendStore {
required this.name,
required this.numberOfTiles,
required this.numberOfBytes,
required this.hits,
required this.misses,
});
}

Expand Down
53 changes: 53 additions & 0 deletions lib/src/backend/impls/objectbox/worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ enum _WorkerKey {
deleteStore,
getStoreSize,
getStoreLength,
getStoreHits,
getStoreMisses,
readTile,
writeTile,
deleteTile,
Expand Down Expand Up @@ -68,6 +70,8 @@ Future<void> _worker(
name: cmd.args['storeName']! as String,
numberOfTiles: 0,
numberOfBytes: 0,
hits: 0,
misses: 0,
),
mode: PutMode.insert,
);
Expand All @@ -78,6 +82,7 @@ Future<void> _worker(
final removeIds = <int>[];

final tiles = root.box<ObjectBoxTile>();
final stores = root.box<ObjectBoxStore>();

final tilesQuery = (tiles.query()
..linkMany(
Expand All @@ -86,6 +91,9 @@ Future<void> _worker(
))
.build();

final storeQuery =
stores.query(ObjectBoxStore_.name.equals(storeName)).build();

root.runInTransaction(
TxMode.write,
() {
Expand All @@ -107,6 +115,21 @@ Future<void> _worker(
tiles.query(ObjectBoxTile_.id.oneOf(removeIds)).build()
..remove()
..close();

final store = storeQuery.findUnique() ??
(throw StoreUnavailable(storeName: storeName));
storeQuery.close();

assert(store.tiles.isEmpty);

stores.put(
store
..tiles.clear()
..numberOfTiles = 0
..numberOfBytes = 0
..hits = 0
..misses = 0,
);
},
);
sendRes((key: cmd.key, data: null));
Expand Down Expand Up @@ -168,6 +191,34 @@ Future<void> _worker(

sendRes((key: cmd.key, data: {'length': length}));
break;
case _WorkerKey.getStoreHits:
final storeName = cmd.args['storeName']! as String;

final query = root
.box<ObjectBoxStore>()
.query(ObjectBoxStore_.name.equals(storeName))
.build();
final hits = (query.findUnique() ??
(throw StoreUnavailable(storeName: storeName)))
.hits;
query.close();

sendRes((key: cmd.key, data: {'hits': hits}));
break;
case _WorkerKey.getStoreMisses:
final storeName = cmd.args['storeName']! as String;

final query = root
.box<ObjectBoxStore>()
.query(ObjectBoxStore_.name.equals(storeName))
.build();
final misses = (query.findUnique() ??
(throw StoreUnavailable(storeName: storeName)))
.misses;
query.close();

sendRes((key: cmd.key, data: {'misses': misses}));
break;
case _WorkerKey.readTile:
final query = root
.box<ObjectBoxTile>()
Expand All @@ -176,6 +227,8 @@ Future<void> _worker(
final tile = query.findUnique();
query.close();

// TODO: Hits & misses

sendRes((key: cmd.key, data: {'tile': tile}));
break;
case _WorkerKey.writeTile:
Expand Down
40 changes: 37 additions & 3 deletions lib/src/backend/interfaces/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ import 'models.dart';
/// To end-users:
/// * Use [FMTCSettings.backend] to set a custom backend
/// * Not all sync versions of methods are guaranteed to have implementations
/// * Never access the [internal] method of a backend
abstract interface class FMTCBackend {
/// * Avoid calling the [internal] method of a backend
abstract interface class FMTCBackend<Internal extends FMTCBackendInternal> {
const FMTCBackend();

@protected
FMTCBackendInternal get internal;
Internal get internal;
}

/// An abstract interface that FMTC will use to communicate with a storage
Expand Down Expand Up @@ -202,6 +202,40 @@ abstract interface class FMTCBackendInternal {
/// If `false`, calling will throw an [SyncOperationUnsupported] error.
abstract final bool supportsSyncGetStoreLength;

/// Retrieve the number of times that a tile was successfully retrieved from
/// the specified store when browsing
Future<int> getStoreHits({
required String storeName,
});

/// Retrieve the number of times that a tile was successfully retrieved from
/// the specified store when browsing
int getStoreHitsSync({
required String storeName,
});

/// Whether [getStoreHitsSync] is implemented
///
/// If `false`, calling will throw an [SyncOperationUnsupported] error.
abstract final bool supportsSyncGetStoreHits;

/// Retrieve the number of times that a tile was attempted to be retrieved from
/// the specified store when browsing, but was not present
Future<int> getStoreMisses({
required String storeName,
});

/// Retrieve the number of times that a tile was attempted to be retrieved from
/// the specified store when browsing, but was not present
int getStoreMissesSync({
required String storeName,
});

/// Whether [getStoreMissesSync] is implemented
///
/// If `false`, calling will throw an [SyncOperationUnsupported] error.
abstract final bool supportsSyncGetStoreMisses;

/// Get a raw tile by URL
Future<BackendTile?> readTile({
required String url,
Expand Down
Loading

0 comments on commit f9f4009

Please sign in to comment.