mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-01-15 09:37:09 -05:00
spine update
This commit is contained in:
parent
ddc02f6771
commit
68aad4a94e
@ -61,48 +61,39 @@ Future<void> Function() makeTestDHTShortArrayAdd({required int stride}) =>
|
|||||||
|
|
||||||
print('adding singles\n');
|
print('adding singles\n');
|
||||||
{
|
{
|
||||||
final res = await arr.operateWrite((w) async {
|
|
||||||
for (var n = 4; n < 8; n++) {
|
for (var n = 4; n < 8; n++) {
|
||||||
|
await arr.operateWriteEventual((w) async {
|
||||||
print('$n ');
|
print('$n ');
|
||||||
final success = await w.tryAdd(dataset[n]);
|
return w.tryAdd(dataset[n]);
|
||||||
expect(success, isTrue);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
expect(res, isNull);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print('adding batch\n');
|
print('adding batch\n');
|
||||||
{
|
{
|
||||||
final res = await arr.operateWrite((w) async {
|
await arr.operateWriteEventual((w) async {
|
||||||
print('${dataset.length ~/ 2}-${dataset.length}');
|
print('${dataset.length ~/ 2}-${dataset.length}');
|
||||||
final success = await w
|
return w
|
||||||
.tryAddAll(dataset.sublist(dataset.length ~/ 2, dataset.length));
|
.tryAddAll(dataset.sublist(dataset.length ~/ 2, dataset.length));
|
||||||
expect(success, isTrue);
|
|
||||||
});
|
});
|
||||||
expect(res, isNull);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print('inserting singles\n');
|
print('inserting singles\n');
|
||||||
{
|
{
|
||||||
final res = await arr.operateWrite((w) async {
|
|
||||||
for (var n = 0; n < 4; n++) {
|
for (var n = 0; n < 4; n++) {
|
||||||
|
await arr.operateWriteEventual((w) async {
|
||||||
print('$n ');
|
print('$n ');
|
||||||
final success = await w.tryInsert(n, dataset[n]);
|
return w.tryInsert(n, dataset[n]);
|
||||||
expect(success, isTrue);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
expect(res, isNull);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print('inserting batch\n');
|
print('inserting batch\n');
|
||||||
{
|
{
|
||||||
final res = await arr.operateWrite((w) async {
|
await arr.operateWriteEventual((w) async {
|
||||||
print('8-${dataset.length ~/ 2}');
|
print('8-${dataset.length ~/ 2}');
|
||||||
final success =
|
return w.tryInsertAll(8, dataset.sublist(8, dataset.length ~/ 2));
|
||||||
await w.tryInsertAll(8, dataset.sublist(8, dataset.length ~/ 2));
|
|
||||||
expect(success, isTrue);
|
|
||||||
});
|
});
|
||||||
expect(res, isNull);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//print('get all\n');
|
//print('get all\n');
|
||||||
|
@ -234,7 +234,8 @@ class _DHTLogSpine {
|
|||||||
final existingData = await _spineRecord.tryWriteBytes(headBuffer);
|
final existingData = await _spineRecord.tryWriteBytes(headBuffer);
|
||||||
if (existingData != null) {
|
if (existingData != null) {
|
||||||
// Head write failed, incorporate update
|
// Head write failed, incorporate update
|
||||||
await _updateHead(proto.DHTLog.fromBuffer(existingData));
|
final existingHead = proto.DHTLog.fromBuffer(existingData);
|
||||||
|
_updateHead(existingHead.head, existingHead.tail, old: old);
|
||||||
if (old != null) {
|
if (old != null) {
|
||||||
sendUpdate(old.$1, old.$2);
|
sendUpdate(old.$1, old.$2);
|
||||||
}
|
}
|
||||||
@ -258,11 +259,22 @@ class _DHTLogSpine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Validate a new spine head subkey that has come in from the network
|
/// Validate a new spine head subkey that has come in from the network
|
||||||
Future<void> _updateHead(proto.DHTLog spineHead) async {
|
void _updateHead(int newHead, int newTail, {(int, int)? old}) {
|
||||||
assert(_spineMutex.isLocked, 'should be in mutex here');
|
assert(_spineMutex.isLocked, 'should be in mutex here');
|
||||||
|
|
||||||
_head = spineHead.head;
|
if (old != null) {
|
||||||
_tail = spineHead.tail;
|
final oldHead = old.$1;
|
||||||
|
final oldTail = old.$2;
|
||||||
|
|
||||||
|
final headDelta = _ringDistance(newHead, oldHead);
|
||||||
|
final tailDelta = _ringDistance(newTail, oldTail);
|
||||||
|
if (headDelta > _positionLimit ~/ 2 || tailDelta > _positionLimit ~/ 2) {
|
||||||
|
throw DHTExceptionInvalidData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_head = newHead;
|
||||||
|
_tail = newTail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
@ -456,7 +468,8 @@ class _DHTLogSpine {
|
|||||||
// API for public interfaces
|
// API for public interfaces
|
||||||
|
|
||||||
Future<_DHTLogPosition?> lookupPositionBySegmentNumber(
|
Future<_DHTLogPosition?> lookupPositionBySegmentNumber(
|
||||||
int segmentNumber, int segmentPos) async =>
|
int segmentNumber, int segmentPos,
|
||||||
|
{bool onlyOpened = false}) async =>
|
||||||
_spineCacheMutex.protect(() async {
|
_spineCacheMutex.protect(() async {
|
||||||
// Get the segment shortArray
|
// Get the segment shortArray
|
||||||
final openedSegment = _openedSegments[segmentNumber];
|
final openedSegment = _openedSegments[segmentNumber];
|
||||||
@ -465,6 +478,10 @@ class _DHTLogSpine {
|
|||||||
openedSegment.openCount++;
|
openedSegment.openCount++;
|
||||||
shortArray = openedSegment.shortArray;
|
shortArray = openedSegment.shortArray;
|
||||||
} else {
|
} else {
|
||||||
|
if (onlyOpened) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final newShortArray = (_spineRecord.writer == null)
|
final newShortArray = (_spineRecord.writer == null)
|
||||||
? await _openSegment(segmentNumber)
|
? await _openSegment(segmentNumber)
|
||||||
: await _openOrCreateSegment(segmentNumber);
|
: await _openOrCreateSegment(segmentNumber);
|
||||||
@ -665,28 +682,30 @@ class _DHTLogSpine {
|
|||||||
final headData = proto.DHTLog.fromBuffer(data);
|
final headData = proto.DHTLog.fromBuffer(data);
|
||||||
|
|
||||||
// Then update the head record
|
// Then update the head record
|
||||||
|
_spineChangeProcessor.updateState(headData, (headData) async {
|
||||||
await _spineMutex.protect(() async {
|
await _spineMutex.protect(() async {
|
||||||
final oldHead = _head;
|
final oldHead = _head;
|
||||||
final oldTail = _tail;
|
final oldTail = _tail;
|
||||||
await _updateHead(headData);
|
|
||||||
|
_updateHead(headData.head, headData.tail, old: (oldHead, oldTail));
|
||||||
|
|
||||||
// Lookup tail position segments that have changed
|
// Lookup tail position segments that have changed
|
||||||
// and force their short arrays to refresh their heads
|
// and force their short arrays to refresh their heads if
|
||||||
|
// they are opened
|
||||||
final segmentsToRefresh = <_DHTLogPosition>[];
|
final segmentsToRefresh = <_DHTLogPosition>[];
|
||||||
int? lastSegmentNumber;
|
|
||||||
for (var curTail = oldTail;
|
for (var curTail = oldTail;
|
||||||
curTail != _tail;
|
curTail != _tail;
|
||||||
curTail = (curTail + 1) % _positionLimit) {
|
curTail = (curTail +
|
||||||
|
(DHTShortArray.maxElements -
|
||||||
|
(curTail % DHTShortArray.maxElements))) %
|
||||||
|
_positionLimit) {
|
||||||
final segmentNumber = curTail ~/ DHTShortArray.maxElements;
|
final segmentNumber = curTail ~/ DHTShortArray.maxElements;
|
||||||
final segmentPos = curTail % DHTShortArray.maxElements;
|
final segmentPos = curTail % DHTShortArray.maxElements;
|
||||||
if (segmentNumber == lastSegmentNumber) {
|
final dhtLogPosition = await lookupPositionBySegmentNumber(
|
||||||
continue;
|
segmentNumber, segmentPos,
|
||||||
}
|
onlyOpened: true);
|
||||||
lastSegmentNumber = segmentNumber;
|
|
||||||
final dhtLogPosition =
|
|
||||||
await lookupPositionBySegmentNumber(segmentNumber, segmentPos);
|
|
||||||
if (dhtLogPosition == null) {
|
if (dhtLogPosition == null) {
|
||||||
throw Exception('missing segment in dht log');
|
continue;
|
||||||
}
|
}
|
||||||
segmentsToRefresh.add(dhtLogPosition);
|
segmentsToRefresh.add(dhtLogPosition);
|
||||||
}
|
}
|
||||||
@ -699,6 +718,7 @@ class _DHTLogSpine {
|
|||||||
|
|
||||||
sendUpdate(oldHead, oldTail);
|
sendUpdate(oldHead, oldTail);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@ -723,6 +743,8 @@ class _DHTLogSpine {
|
|||||||
StreamSubscription<DHTRecordWatchChange>? _subscription;
|
StreamSubscription<DHTRecordWatchChange>? _subscription;
|
||||||
// Notify closure for external spine head changes
|
// Notify closure for external spine head changes
|
||||||
void Function(DHTLogUpdate)? onUpdatedSpine;
|
void Function(DHTLogUpdate)? onUpdatedSpine;
|
||||||
|
// Single state processor for spine updates
|
||||||
|
final _spineChangeProcessor = SingleStateProcessor<proto.DHTLog>();
|
||||||
|
|
||||||
// Spine DHT record
|
// Spine DHT record
|
||||||
final DHTRecord _spineRecord;
|
final DHTRecord _spineRecord;
|
||||||
|
@ -3,3 +3,8 @@ class DHTExceptionTryAgain implements Exception {
|
|||||||
[this.cause = 'operation failed due to newer dht value']);
|
[this.cause = 'operation failed due to newer dht value']);
|
||||||
String cause;
|
String cause;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DHTExceptionInvalidData implements Exception {
|
||||||
|
DHTExceptionInvalidData([this.cause = 'dht data structure is corrupt']);
|
||||||
|
String cause;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user