better message status support

This commit is contained in:
Christien Rioux 2024-04-17 21:31:26 -04:00
parent 4f02435964
commit 809f6d69bf
31 changed files with 1046 additions and 248 deletions

View file

@ -86,7 +86,7 @@ class DHTRecord {
if (_open) {
await close();
}
await DHTRecordPool.instance.delete(key);
await DHTRecordPool.instance.deleteRecord(key);
rethrow;
}
}

View file

@ -109,7 +109,7 @@ class OpenedRecordInfo {
String get sharedDetails => shared.toString();
}
class DHTRecordPool with TableDBBacked<DHTRecordPoolAllocations> {
class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
DHTRecordPool._(Veilid veilid, VeilidRoutingContext routingContext)
: _state = const DHTRecordPoolAllocations(),
_mutex = Mutex(),
@ -150,7 +150,7 @@ class DHTRecordPool with TableDBBacked<DHTRecordPoolAllocations> {
? DHTRecordPoolAllocations.fromJson(obj)
: const DHTRecordPoolAllocations();
@override
Object? valueToJson(DHTRecordPoolAllocations val) => val.toJson();
Object? valueToJson(DHTRecordPoolAllocations? val) => val?.toJson();
//////////////////////////////////////////////////////////////
@ -161,7 +161,7 @@ class DHTRecordPool with TableDBBacked<DHTRecordPoolAllocations> {
final globalPool = DHTRecordPool._(Veilid.instance, routingContext);
globalPool
.._logger = logger
.._state = await globalPool.load();
.._state = await globalPool.load() ?? const DHTRecordPoolAllocations();
_singleton = globalPool;
}
@ -279,7 +279,7 @@ class DHTRecordPool with TableDBBacked<DHTRecordPoolAllocations> {
if (openedRecordInfo.records.isEmpty) {
await _routingContext.closeDHTRecord(key);
if (openedRecordInfo.shared.deleteOnClose) {
await _deleteInner(key);
await _deleteRecordInner(key);
}
_opened.remove(key);
}
@ -316,7 +316,7 @@ class DHTRecordPool with TableDBBacked<DHTRecordPoolAllocations> {
}
}
Future<void> _deleteInner(TypedKey recordKey) async {
Future<void> _deleteRecordInner(TypedKey recordKey) async {
log('deleteDHTRecord: key=$recordKey');
// Remove this child from parents
@ -324,7 +324,7 @@ class DHTRecordPool with TableDBBacked<DHTRecordPoolAllocations> {
await _routingContext.deleteDHTRecord(recordKey);
}
Future<void> delete(TypedKey recordKey) async {
Future<void> deleteRecord(TypedKey recordKey) async {
await _mutex.protect(() async {
final allDeps = _collectChildrenInner(recordKey);
@ -339,7 +339,7 @@ class DHTRecordPool with TableDBBacked<DHTRecordPoolAllocations> {
ori.shared.deleteOnClose = true;
} else {
// delete now
await _deleteInner(recordKey);
await _deleteRecordInner(recordKey);
}
});
}

View file

@ -69,7 +69,7 @@ class DHTShortArray {
return dhtShortArray;
} on Exception catch (_) {
await dhtRecord.close();
await pool.delete(dhtRecord.key);
await pool.deleteRecord(dhtRecord.key);
rethrow;
}
}
@ -152,7 +152,7 @@ class DHTShortArray {
/// Free all resources for the DHTShortArray and delete it from the DHT
Future<void> delete() async {
await close();
await DHTRecordPool.instance.delete(recordKey);
await DHTRecordPool.instance.deleteRecord(recordKey);
}
/// Runs a closure that guarantees the DHTShortArray
@ -212,6 +212,8 @@ class DHTShortArray {
return closure(writer);
}, timeout: timeout);
/// Listen to and any all changes to the structure of this short array
/// regardless of where the changes are coming from
Future<StreamSubscription<void>> listen(
void Function() onChanged,
) =>

View file

@ -3,11 +3,24 @@ import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:bloc/bloc.dart';
import 'package:bloc_tools/bloc_tools.dart';
import 'package:equatable/equatable.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:meta/meta.dart';
import '../../../veilid_support.dart';
typedef DHTShortArrayState<T> = AsyncValue<IList<T>>;
@immutable
class DHTShortArrayElementState<T> extends Equatable {
const DHTShortArrayElementState(
{required this.value, required this.isOffline});
final T value;
final bool isOffline;
@override
List<Object?> get props => [value, isOffline];
}
typedef DHTShortArrayState<T> = AsyncValue<IList<DHTShortArrayElementState<T>>>;
typedef DHTShortArrayBusyState<T> = BlocBusyState<DHTShortArrayState<T>>;
class DHTShortArrayCubit<T> extends Cubit<DHTShortArrayBusyState<T>>
@ -49,13 +62,19 @@ class DHTShortArrayCubit<T> extends Cubit<DHTShortArrayBusyState<T>>
Future<void> _refreshNoWait({bool forceRefresh = false}) async =>
busy((emit) async => _refreshInner(emit, forceRefresh: forceRefresh));
Future<void> _refreshInner(void Function(AsyncValue<IList<T>>) emit,
Future<void> _refreshInner(void Function(DHTShortArrayState<T>) emit,
{bool forceRefresh = false}) async {
try {
final newState = (await _shortArray.operate(
(reader) => reader.getAllItems(forceRefresh: forceRefresh)))
?.map(_decodeElement)
.toIList();
final newState = await _shortArray.operate((reader) async {
final offlinePositions = await reader.getOfflinePositions();
final allItems = (await reader.getAllItems(forceRefresh: forceRefresh))
?.indexed
.map((x) => DHTShortArrayElementState(
value: _decodeElement(x.$2),
isOffline: offlinePositions.contains(x.$1)))
.toIList();
return allItems;
});
if (newState != null) {
emit(AsyncValue.data(newState));
}

View file

@ -15,6 +15,9 @@ abstract class DHTShortArrayRead {
/// is specified, the network will always be checked for newer values
/// rather than returning the existing locally stored copy of the elements.
Future<List<Uint8List>?> getAllItems({bool forceRefresh = false});
/// Get a list of the positions that were written offline and not flushed yet
Future<Set<int>> getOfflinePositions();
}
extension DHTShortArrayReadExt on DHTShortArrayRead {
@ -96,6 +99,40 @@ class _DHTShortArrayRead implements DHTShortArrayRead {
return out;
}
/// Get a list of the positions that were written offline and not flushed yet
@override
Future<Set<int>> getOfflinePositions() async {
final indexOffline = <int>{};
final inspects = await [
_head._headRecord.inspect(),
..._head._linkedRecords.map((lr) => lr.inspect())
].wait;
// Add to offline index
var strideOffset = 0;
for (final inspect in inspects) {
for (final r in inspect.offlineSubkeys) {
for (var i = r.low; i <= r.high; i++) {
// If this is the head record, ignore the first head subkey
if (strideOffset != 0 || i != 0) {
indexOffline.add(i + ((strideOffset == 0) ? -1 : strideOffset));
}
}
}
strideOffset += _head._stride;
}
// See which positions map to offline indexes
final positionOffline = <int>{};
for (var i = 0; i < _head._index.length; i++) {
final idx = _head._index[i];
if (indexOffline.contains(idx)) {
positionOffline.add(i);
}
}
return positionOffline;
}
////////////////////////////////////////////////////////////////////////////
// Fields
final _DHTShortArrayHead _head;

View file

@ -92,12 +92,11 @@ extension DHTShortArrayWriteExt on DHTShortArrayWrite {
}
////////////////////////////////////////////////////////////////////////////
// Writer-only implementation
// Writer implementation
class _DHTShortArrayWrite implements DHTShortArrayWrite {
_DHTShortArrayWrite._(_DHTShortArrayHead head)
: _head = head,
_reader = _DHTShortArrayRead._(head);
class _DHTShortArrayWrite extends _DHTShortArrayRead
implements DHTShortArrayWrite {
_DHTShortArrayWrite._(super.head) : super._();
@override
Future<bool> tryAddItem(Uint8List value) async {
@ -187,23 +186,4 @@ class _DHTShortArrayWrite implements DHTShortArrayWrite {
}
return (oldValue, true);
}
////////////////////////////////////////////////////////////////////////////
// Reader passthrough
@override
int get length => _reader.length;
@override
Future<Uint8List?> getItem(int pos, {bool forceRefresh = false}) =>
_reader.getItem(pos, forceRefresh: forceRefresh);
@override
Future<List<Uint8List>?> getAllItems({bool forceRefresh = false}) =>
_reader.getAllItems(forceRefresh: forceRefresh);
////////////////////////////////////////////////////////////////////////////
// Fields
final _DHTShortArrayHead _head;
final _DHTShortArrayRead _reader;
}