public SequenceFileStorage(ILoggerFactory loggerFactory, string nodeId) { _logger = loggerFactory.CreateLogger <SequenceFileStorage>(); _dataFileName = $"{nodeId}-data.bin"; _indexFileName = $"{nodeId}-data.idx"; _sempaphore.Wait(); try { if (_dataFS == null || _indexFS == null) { _dataFS = File.Open(_dataFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); _indexFS = File.Open(_indexFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); byte[] indexBytes = new byte[BusinessCodeIndex.BYTES_LENGTH]; while (_indexFS.Read(indexBytes, 0, indexBytes.Length) > 0) { var code = new BusinessCodeIndex(indexBytes); _indexQueue.Enqueue(code); } } } finally { _sempaphore.Release(); } }
public async Task WriteAsync(BusinessCode codeToFlush, long globalVersion) { if (codeToFlush == null) { return; } _sempaphore.Wait(); try { byte[] dataBytes = new byte[BusinessCode.BYTES_LENGTH]; var code = new BusinessCode(codeToFlush.ToBytes());// 防止2次读取不一致 var existsCodeIndex = _indexQueue.FirstOrDefault(m => m.ApplicationID == code.ApplicationId && m.CodeType == code.CodeType); if (existsCodeIndex == null) { _dataFS.Seek(0, SeekOrigin.End); var indexPosition = _dataFS.Position; dataBytes = code.ToBytes(); await _dataFS.WriteAsync(dataBytes, 0, dataBytes.Length); var codeIndex = new BusinessCodeIndex { ApplicationID = code.ApplicationId, CodeType = code.CodeType, Position = indexPosition }; var codeIndexBytes = codeIndex.ToBytes(); await _indexFS.WriteAsync(codeIndexBytes, 0, codeIndexBytes.Length); _indexQueue.Enqueue(codeIndex); } else { _dataFS.Seek(existsCodeIndex.Position, SeekOrigin.Begin); _dataFS.Read(dataBytes, 0, dataBytes.Length); var currentCode = new BusinessCode(dataBytes); if (code.ApplicationId != currentCode.ApplicationId || code.CodeType != currentCode.CodeType) { _logger.LogError($"Bad file {_dataFileName}, applicationId={codeToFlush.ApplicationId}, codeType={codeToFlush.CodeType}"); throw new InvalidDataException($"Bad file {_dataFileName}"); } if (code.ApplicationId == currentCode.ApplicationId && code.CodeType == currentCode.CodeType) { if (code.VersionId < currentCode.VersionId) { _logger.LogError($"Bad file {_dataFileName}, version is invalid, applicationId={codeToFlush.ApplicationId}, codeType={codeToFlush.CodeType}, versionId={codeToFlush.VersionId}"); throw new InvalidDataException($"Bad file {_dataFileName}"); } if (code.Infix != currentCode.Infix || code.Sequence != currentCode.Sequence) { if (code.Infix == currentCode.Infix && code.Sequence < currentCode.Sequence) { _logger.LogError($"Bad file {_dataFileName}, sequence is invalid, applicationId={codeToFlush.ApplicationId}, codeType={codeToFlush.CodeType}, sequence={codeToFlush.Sequence}"); throw new InvalidDataException($"Bad file {_dataFileName}"); } _dataFS.Seek(existsCodeIndex.Position, SeekOrigin.Begin); dataBytes = code.ToBytes(); await _dataFS.WriteAsync(dataBytes, 0, dataBytes.Length); } } } // 写入全局版本号 _dataFS.Seek(0, SeekOrigin.Begin); await _dataFS.WriteAsync(BitConverter.GetBytes(globalVersion), 0, 8); } catch (Exception ex) { _logger.LogError($"Write fail: {ex.Message}, applicationId={codeToFlush.ApplicationId}, codeType={codeToFlush.CodeType}", ex); throw; } finally { _sempaphore.Release(); } }