|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using ZeroLevel.Services.FileSystem;
|
|
|
|
|
using ZeroLevel.Services.Memory;
|
|
|
|
|
using ZeroLevel.Services.PartitionStorage.Interfaces;
|
|
|
|
|
using ZeroLevel.Services.Serialization;
|
|
|
|
|
|
|
|
|
|
namespace ZeroLevel.Services.PartitionStorage.Partition
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// General operations with a partition
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal abstract class BasePartition<TKey, TInput, TValue, TMeta>
|
|
|
|
|
: IStorePartitionBase<TKey, TInput, TValue>
|
|
|
|
|
{
|
|
|
|
|
public string Catalog { get { return _catalog; } }
|
|
|
|
|
|
|
|
|
|
protected readonly TMeta _info;
|
|
|
|
|
protected readonly string _catalog;
|
|
|
|
|
|
|
|
|
|
private SemaphoreSlim _writersLock = new SemaphoreSlim(1);
|
|
|
|
|
private readonly Dictionary<string, MemoryStreamWriter> _writeStreams = new Dictionary<string, MemoryStreamWriter>();
|
|
|
|
|
|
|
|
|
|
protected IStoreSerializer<TKey, TInput, TValue> Serializer { get; }
|
|
|
|
|
protected readonly StoreOptions<TKey, TInput, TValue, TMeta> _options;
|
|
|
|
|
|
|
|
|
|
private readonly IndexBuilder<TKey, TValue> _indexBuilder;
|
|
|
|
|
|
|
|
|
|
private readonly PhisicalFileAccessorCachee _phisicalFileAccessor;
|
|
|
|
|
protected PhisicalFileAccessorCachee PhisicalFileAccessorCachee => _phisicalFileAccessor;
|
|
|
|
|
|
|
|
|
|
internal BasePartition(StoreOptions<TKey, TInput, TValue, TMeta> options,
|
|
|
|
|
TMeta info,
|
|
|
|
|
IStoreSerializer<TKey, TInput, TValue> serializer, PhisicalFileAccessorCachee fileAccessorCachee)
|
|
|
|
|
{
|
|
|
|
|
_options = options;
|
|
|
|
|
_info = info;
|
|
|
|
|
_catalog = _options.GetCatalogPath(info);
|
|
|
|
|
if (Directory.Exists(_catalog) == false)
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(_catalog);
|
|
|
|
|
}
|
|
|
|
|
_phisicalFileAccessor = fileAccessorCachee;
|
|
|
|
|
Serializer = serializer;
|
|
|
|
|
_indexBuilder = _options.Index.Enabled ? new IndexBuilder<TKey, TValue>(_options.Index.StepType, _options.Index.StepValue, _catalog, fileAccessorCachee, Serializer) : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region IStorePartitionBase
|
|
|
|
|
public int CountDataFiles() => Directory.Exists(_catalog) ? (Directory.GetFiles(_catalog)?.Length ?? 0) : 0;
|
|
|
|
|
public string GetCatalogPath() => _catalog;
|
|
|
|
|
public void DropData() => FSUtils.CleanAndTestFolder(_catalog);
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
CloseWriteStreams();
|
|
|
|
|
Release();
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public abstract void Release();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Rebuild indexes for all files
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected async Task RebuildIndexes()
|
|
|
|
|
{
|
|
|
|
|
if (_options.Index.Enabled)
|
|
|
|
|
{
|
|
|
|
|
await _indexBuilder.RebuildIndex();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Rebuild index for the specified file
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal async Task RebuildFileIndex(string file)
|
|
|
|
|
{
|
|
|
|
|
if (_options.Index.Enabled)
|
|
|
|
|
{
|
|
|
|
|
await _indexBuilder.RebuildFileIndex(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Delete the index for the specified file
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal void DropFileIndex(string file)
|
|
|
|
|
{
|
|
|
|
|
if (_options.Index.Enabled)
|
|
|
|
|
{
|
|
|
|
|
_indexBuilder.DropFileIndex(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Close all streams for writing
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected void CloseWriteStreams()
|
|
|
|
|
{
|
|
|
|
|
foreach (var s in _writeStreams)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
s.Value.Stream.Flush();
|
|
|
|
|
s.Value.Dispose();
|
|
|
|
|
s.Value.DisposeAsync();
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
_writeStreams.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected async Task WriteStreamAction(string fileName, Func<MemoryStreamWriter, Task> writeAction)
|
|
|
|
|
{
|
|
|
|
|
MemoryStreamWriter writer;
|
|
|
|
|
if (_writeStreams.TryGetValue(fileName, out writer) == false)
|
|
|
|
|
{
|
|
|
|
|
await _writersLock.WaitAsync();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_writeStreams.TryGetValue(fileName, out writer) == false)
|
|
|
|
|
{
|
|
|
|
|
var filePath = Path.Combine(_catalog, fileName);
|
|
|
|
|
var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, 4096 * 1024);
|
|
|
|
|
var new_w = new MemoryStreamWriter(stream);
|
|
|
|
|
_writeStreams[fileName] = new_w;
|
|
|
|
|
writer = new_w;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_writersLock.Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
await writeAction.Invoke(writer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected async Task SafeWriteStreamAction(string fileName, Func<MemoryStreamWriter, Task> writeAction)
|
|
|
|
|
{
|
|
|
|
|
MemoryStreamWriter writer;
|
|
|
|
|
if (_writeStreams.TryGetValue(fileName, out writer) == false)
|
|
|
|
|
{
|
|
|
|
|
await _writersLock.WaitAsync();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_writeStreams.TryGetValue(fileName, out writer) == false)
|
|
|
|
|
{
|
|
|
|
|
var filePath = Path.Combine(_catalog, fileName);
|
|
|
|
|
var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, 4096 * 1024);
|
|
|
|
|
var new_w = new MemoryStreamWriter(stream);
|
|
|
|
|
_writeStreams[fileName] = new_w;
|
|
|
|
|
writer = new_w;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
_writersLock.Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
await writer.WaitLockAsync();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await writeAction.Invoke(writer);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
writer.Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempting to open a file for writing
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected bool TryGetWriteStream(string fileName, out MemoryStreamWriter writer)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
bool taken = false;
|
|
|
|
|
Monitor.Enter(_writeStreams, ref taken);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_writeStreams.TryGetValue(fileName, out var w))
|
|
|
|
|
{
|
|
|
|
|
writer = w;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var filePath = Path.Combine(_catalog, fileName);
|
|
|
|
|
var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None, 4096 * 1024);
|
|
|
|
|
var new_w = new MemoryStreamWriter(stream);
|
|
|
|
|
_writeStreams[fileName] = new_w;
|
|
|
|
|
writer = new_w;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (taken)
|
|
|
|
|
{
|
|
|
|
|
Monitor.Exit(_writeStreams);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, "[StorePartitionBuilder.TryGetWriteStream]");
|
|
|
|
|
}
|
|
|
|
|
writer = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempting to open a file for reading
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected bool TryGetReadStream(string fileName, out MemoryStreamReader reader)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var filePath = Path.Combine(_catalog, fileName);
|
|
|
|
|
var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096 * 1024);
|
|
|
|
|
reader = new MemoryStreamReader(stream);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, "[StorePartitionBuilder.TryGetReadStream]");
|
|
|
|
|
}
|
|
|
|
|
reader = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
protected IViewAccessor GetViewAccessor(TKey key, long offset)
|
|
|
|
|
{
|
|
|
|
|
var fileName = _options.GetFileName(key, _info);
|
|
|
|
|
var filePath = Path.Combine(_catalog, fileName);
|
|
|
|
|
return GetViewAccessor(filePath, offset);
|
|
|
|
|
}
|
|
|
|
|
protected IViewAccessor GetViewAccessor(TKey key, long offset, int length)
|
|
|
|
|
{
|
|
|
|
|
var fileName = _options.GetFileName(key, _info);
|
|
|
|
|
var filePath = Path.Combine(_catalog, fileName);
|
|
|
|
|
return GetViewAccessor(filePath, offset, length);
|
|
|
|
|
}
|
|
|
|
|
protected IViewAccessor GetViewAccessor(string filePath, long offset)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return PhisicalFileAccessorCachee.GetDataAccessor(filePath, offset);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[StorePartitionAccessor.GetViewAccessor] '{filePath}'");
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
protected IViewAccessor GetViewAccessor(string filePath, long offset, int length)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return PhisicalFileAccessorCachee.GetDataAccessor(filePath, offset, length);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.SystemError(ex, $"[StorePartitionAccessor.GetViewAccessor] '{filePath}'");
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|