Skip to content

Commit 61b7473

Browse files
committed
Fixing issue with SeekShortChunks
1 parent be05af6 commit 61b7473

1 file changed

Lines changed: 41 additions & 24 deletions

File tree

LiteDBX/Client/Storage/LiteFileHandle.cs

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ internal sealed class LiteFileHandle<TFileId> : ILiteFileHandle<TFileId>
6363
// ── Write state ───────────────────────────────────────────────────────────
6464

6565
private MemoryStream _writeBuffer;
66-
private bool _finalized;
66+
private bool _writeStateDirty;
6767

6868
// ── Construction ─────────────────────────────────────────────────────────
6969

@@ -134,11 +134,9 @@ public async ValueTask<int> Read(Memory<byte> buffer, CancellationToken cancella
134134
if (buffer.IsEmpty || _position >= FileInfo.Length)
135135
return 0;
136136

137-
var targetChunkIndex = EstimateChunkIndex(_position);
138-
139-
if (_currentChunkIndex != targetChunkIndex || _currentChunkData == null)
137+
if (_currentChunkData == null)
140138
{
141-
await LoadChunk(targetChunkIndex, cancellationToken).ConfigureAwait(false);
139+
await LoadChunkForPosition(_position, cancellationToken).ConfigureAwait(false);
142140
}
143141

144142
if (_currentChunkData == null)
@@ -189,6 +187,7 @@ public async ValueTask Write(ReadOnlyMemory<byte> buffer, CancellationToken canc
189187
if (buffer.IsEmpty)
190188
return;
191189

190+
_writeStateDirty = true;
192191
_position += buffer.Length;
193192

194193
// MemoryStream.WriteAsync accepts byte[] rather than ReadOnlyMemory<byte> on .NET Standard 2.x,
@@ -252,7 +251,7 @@ public async ValueTask DisposeAsync()
252251

253252
_disposed = true;
254253

255-
if (CanWrite && !_finalized)
254+
if (CanWrite && (_writeStateDirty || (_writeBuffer?.Length ?? 0) > 0))
256255
{
257256
// Best-effort flush; if the write session was abandoned without an explicit Flush()
258257
// we still attempt to commit whatever data was buffered.
@@ -296,38 +295,56 @@ private async ValueTask LoadChunk(int chunkIndex, CancellationToken cancellation
296295
}
297296

298297
/// <summary>
299-
/// Estimate the chunk index that contains <paramref name="position"/> within the file.
300-
/// Uses cached chunk lengths for accuracy; falls back to uniform-chunk-size division for
301-
/// chunks not yet loaded.
298+
/// Load the exact chunk that contains <paramref name="position"/> and set
299+
/// <see cref="_positionInChunk"/> to the correct intra-chunk offset.
302300
/// </summary>
303-
private int EstimateChunkIndex(long position)
301+
private async ValueTask LoadChunkForPosition(long position, CancellationToken cancellationToken)
304302
{
305-
if (position == 0)
306-
return 0;
303+
if (position < 0 || position >= FileInfo.Length)
304+
{
305+
_currentChunkData = null;
306+
_currentChunkIndex = -1;
307+
_positionInChunk = 0;
308+
return;
309+
}
307310

308-
long cumulative = 0;
311+
long remaining = position;
309312

310-
for (int i = 0; ; i++)
313+
for (var i = 0; i < FileInfo.Chunks; i++)
311314
{
312-
if (_chunkLengths.TryGetValue(i, out var chunkLen))
315+
if (!_chunkLengths.TryGetValue(i, out var chunkLen))
313316
{
314-
if (cumulative + chunkLen > position)
315-
return i;
317+
await LoadChunk(i, cancellationToken).ConfigureAwait(false);
316318

317-
cumulative += chunkLen;
319+
if (_currentChunkData == null)
320+
return;
321+
322+
chunkLen = _currentChunkData.Length;
318323
}
319-
else
324+
325+
if (remaining < chunkLen)
320326
{
321-
// Remaining lengths are unknown; use MaxChunkSize as the estimate.
322-
return i + (int)((position - cumulative) / MaxChunkSize);
327+
if (_currentChunkIndex != i || _currentChunkData == null)
328+
{
329+
await LoadChunk(i, cancellationToken).ConfigureAwait(false);
330+
}
331+
332+
_positionInChunk = (int)remaining;
333+
return;
323334
}
335+
336+
remaining -= chunkLen;
324337
}
338+
339+
_currentChunkData = null;
340+
_currentChunkIndex = -1;
341+
_positionInChunk = 0;
325342
}
326343

327344
/// <summary>
328345
/// Drain <see cref="_writeBuffer"/> into the chunks collection.
329346
/// When <paramref name="finalize"/> is <c>true</c>, updates file metadata in <c>_files</c>
330-
/// and sets <see cref="_finalized"/> so subsequent calls are no-ops.
347+
/// if there have been writes since the last finalize.
331348
/// </summary>
332349
private async ValueTask PersistBufferedChunks(bool finalize, CancellationToken cancellationToken)
333350
{
@@ -364,12 +381,12 @@ private async ValueTask PersistBufferedChunks(bool finalize, CancellationToken c
364381
await _chunks.Insert(chunkDoc, cancellationToken).ConfigureAwait(false);
365382
}
366383

367-
if (finalize && !_finalized)
384+
if (finalize && _writeStateDirty)
368385
{
369-
_finalized = true;
370386
FileInfo.UploadDate = DateTime.UtcNow;
371387
FileInfo.Length = _position;
372388
await _files.Upsert(FileInfo, cancellationToken).ConfigureAwait(false);
389+
_writeStateDirty = false;
373390
}
374391

375392
// Reset the write buffer for subsequent Write() calls before a final Flush().

0 commit comments

Comments
 (0)