private void ReadAllFromDisk() { file.Position = ReadOffsets(Path.GetFileName(streamSource.GetLatestName(dataPath))); while (true) { using (var reader = new BinaryReader(file, Encoding.UTF8, leaveOpen: true)) { var itemPos = file.Position; PersistedEvent persistedEvent; try { persistedEvent = ReadPersistedEvent(reader); eventsCount++; } catch (EndOfStreamException) { lastWriteSnapshotCount = eventsCount; lastWriteSnapshotTime = DateTime.UtcNow; return; } catch (Exception e) { log.WarnException("Error during initial events read, file corrupted", e); if (options.AllowRecovery == false) throw; file.SetLength(itemPos); log.Warn("Recovered from corrupted file by truncating file to last known good position: " + itemPos); return; } switch (persistedEvent.State) { case EventState.Event: case EventState.Snapshot: idToPos.AddOrUpdate(persistedEvent.Id, s => new StreamInformation { LastPosition = itemPos, StreamLength = 1 }, (s, information) => new StreamInformation { LastPosition = itemPos, StreamLength = information.StreamLength + 1 }); break; case EventState.Delete: deleteCount++; var streamInformation = new StreamInformation { LastPosition = Deleted, StreamLength = 0 }; idToPos.AddOrUpdate(persistedEvent.Id, s => streamInformation, (s, information) => { deleteCount += information.StreamLength; return streamInformation; }); break; default: throw new ArgumentOutOfRangeException(persistedEvent.State.ToString()); } } } }
private void HandleFlushSuccessful(WriteState item, long currentPos) { switch (item.State) { case EventState.Event: case EventState.Snapshot: idToPos.AddOrUpdate(item.Id, s => new StreamInformation { LastPosition = currentPos, StreamLength = 1 }, (s, l) => new StreamInformation { LastPosition = currentPos, StreamLength = l.StreamLength + 1 }); break; case EventState.Delete: var streamInformation = new StreamInformation { LastPosition = Deleted, StreamLength = 0 }; idToPos.AddOrUpdate(item.Id, s => streamInformation, (s, l) => { deleteCount += l.StreamLength; return streamInformation; }); break; default: throw new ArgumentOutOfRangeException(item.State.ToString()); } item.TaskCompletionSource.SetResult(null); }
private long ReadOffsets(string currentFileName) { long position; if (streamSource.Exists(offsetsPath) == false) return 0; using (var offsets = streamSource.OpenRead(offsetsPath)) using(var reader = new BinaryReader(offsets)) { var fileName = reader.ReadString(); if(fileName != currentFileName) { // wrong file, skipping streamSource.DeleteOnClose(offsetsPath); return 0; } position = reader.ReadInt64(); while (true) { string key; try { key = reader.ReadString(); } catch (EndOfStreamException) { break; } var pos = reader.ReadInt64(); var count = reader.ReadInt32(); idToPos[key] = new StreamInformation { LastPosition = pos, StreamLength = count }; } } return position; }
public void Compact() { var newPositions = new Dictionary<string, StreamInformation>(StringComparer.InvariantCultureIgnoreCase); var newFilePath = dataPath + ".compacting"; streamSource.DeleteIfExists(newFilePath); using (var newFile = streamSource.OpenReadWrite(newFilePath)) using (var newWriter = new BinaryWriter(newFile)) using (var current = streamSource.OpenRead(dataPath)) using (var reader = new BinaryReader(current)) { while (true) { PersistedEvent persistedEvent; try { persistedEvent = ReadPersistedEvent(reader); } catch (EndOfStreamException) { break; } StreamInformation value; if (idToPos.TryGetValue(persistedEvent.Id, out value) == false) continue; if (value.LastPosition == Deleted || value.LastPosition == DoesNotExists) continue; StreamInformation information; if (newPositions.TryGetValue(persistedEvent.Id, out information) == false) { information =new StreamInformation { LastPosition = DoesNotExists, StreamLength = 0 }; } persistedEvent.Previous = information.LastPosition; newPositions[persistedEvent.Id] = new StreamInformation { LastPosition = newFile.Position, StreamLength = persistedEvent.StreamLength }; WriteItem(persistedEvent, newWriter); } streamSource.Flush(newFile); compactionLock.EnterWriteLock(); try { newFile.Close(); streamSource.DeleteOnClose(dataPath); Stream result; while (cachedReadStreams.TryDequeue(out result)) { result.Dispose(); } binaryWriter.Dispose(); file.Dispose(); streamSource.RenameToLatest(newFilePath, dataPath); file = streamSource.OpenReadWrite(dataPath); binaryWriter = new BinaryWriter(file, Encoding.UTF8, leaveOpen: true); streamSource.DeleteIfExists("data.offsets"); FlushOffsets(); idToPos.Clear(); foreach (var val in newPositions) { idToPos[val.Key] = val.Value; } } finally { compactionLock.ExitWriteLock(); } } }