mirror of
https://github.com/sp-tarkov/loot-dump-processor.git
synced 2025-02-13 09:50:44 -05:00
Refactored storage layer to improve thread safety and code consistency
This commit is contained in:
parent
6cdf3c1a9d
commit
02802ddc10
@ -22,9 +22,6 @@ public static class Program
|
|||||||
|
|
||||||
await using var serviceProvider = services.BuildServiceProvider();
|
await using var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
// Setup Data storage
|
|
||||||
DataStorageFactory.GetInstance().Setup();
|
|
||||||
|
|
||||||
// startup the pipeline
|
// startup the pipeline
|
||||||
var pipeline = serviceProvider.GetRequiredService<IPipeline>();
|
var pipeline = serviceProvider.GetRequiredService<IPipeline>();
|
||||||
await pipeline.Execute();
|
await pipeline.Execute();
|
||||||
|
@ -2,13 +2,14 @@ namespace LootDumpProcessor.Storage;
|
|||||||
|
|
||||||
public abstract class AbstractKey(string[] indexes) : IKey
|
public abstract class AbstractKey(string[] indexes) : IKey
|
||||||
{
|
{
|
||||||
|
private string[] _indexes = indexes;
|
||||||
public abstract KeyType GetKeyType();
|
public abstract KeyType GetKeyType();
|
||||||
|
|
||||||
public string SerializedKey
|
public string SerializedKey
|
||||||
{
|
{
|
||||||
get => string.Join("|", indexes);
|
get => string.Join("|", _indexes);
|
||||||
set => indexes = value.Split("|");
|
set => _indexes = value.Split("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] GetLookupIndex() => indexes;
|
public string[] GetLookupIndex() => _indexes;
|
||||||
}
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
using LootDumpProcessor.Storage.Implementations.File;
|
using LootDumpProcessor.Storage.Implementations.File;
|
||||||
using LootDumpProcessor.Storage.Implementations.Memory;
|
using LootDumpProcessor.Storage.Implementations.Memory;
|
||||||
|
|
||||||
@ -5,9 +6,7 @@ namespace LootDumpProcessor.Storage;
|
|||||||
|
|
||||||
public static class DataStorageFactory
|
public static class DataStorageFactory
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<DataStorageTypes, IDataStorage> _dataStorage = new();
|
private static readonly ConcurrentDictionary<DataStorageTypes, IDataStorage> DataStorage = new();
|
||||||
|
|
||||||
private static object lockObject = new();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requires LootDumpProcessorContext to be initialized before using
|
* Requires LootDumpProcessorContext to be initialized before using
|
||||||
@ -15,23 +14,18 @@ public static class DataStorageFactory
|
|||||||
public static IDataStorage GetInstance() =>
|
public static IDataStorage GetInstance() =>
|
||||||
GetInstance(LootDumpProcessorContext.GetConfig().DataStorageConfig.DataStorageType);
|
GetInstance(LootDumpProcessorContext.GetConfig().DataStorageConfig.DataStorageType);
|
||||||
|
|
||||||
public static IDataStorage GetInstance(DataStorageTypes type)
|
private static IDataStorage GetInstance(DataStorageTypes type)
|
||||||
{
|
{
|
||||||
IDataStorage dataStorage;
|
if (DataStorage.TryGetValue(type, out var dataStorage)) return dataStorage;
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
if (!_dataStorage.TryGetValue(type, out dataStorage))
|
|
||||||
{
|
|
||||||
dataStorage = type switch
|
|
||||||
{
|
|
||||||
DataStorageTypes.File => new FileDataStorage(),
|
|
||||||
DataStorageTypes.Memory => new MemoryDataStorage(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
|
||||||
};
|
|
||||||
|
|
||||||
_dataStorage.Add(type, dataStorage);
|
dataStorage = type switch
|
||||||
}
|
{
|
||||||
}
|
DataStorageTypes.File => new FileDataStorage(),
|
||||||
|
DataStorageTypes.Memory => new MemoryDataStorage(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||||
|
};
|
||||||
|
|
||||||
|
DataStorage.TryAdd(type, dataStorage);
|
||||||
|
|
||||||
return dataStorage;
|
return dataStorage;
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,8 @@ namespace LootDumpProcessor.Storage;
|
|||||||
|
|
||||||
public interface IDataStorage
|
public interface IDataStorage
|
||||||
{
|
{
|
||||||
void Setup();
|
void Store<TEntity>(TEntity entity) where TEntity : IKeyable;
|
||||||
void Store<T>(T t) where T : IKeyable;
|
TEntity? GetItem<TEntity>(IKey key) where TEntity : IKeyable;
|
||||||
bool Exists(IKey t);
|
bool Exists(IKey key);
|
||||||
T GetItem<T>(IKey key) where T : IKeyable;
|
|
||||||
void Clear();
|
void Clear();
|
||||||
}
|
}
|
@ -2,19 +2,14 @@ namespace LootDumpProcessor.Storage.Implementations.File;
|
|||||||
|
|
||||||
public class FileDataStorage : IDataStorage
|
public class FileDataStorage : IDataStorage
|
||||||
{
|
{
|
||||||
public void Setup()
|
public void Store<TEntity>(TEntity entity) where TEntity : IKeyable
|
||||||
{
|
{
|
||||||
|
StoreHandlerFactory.GetInstance(entity.GetKey().GetKeyType()).Store(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Store<T>(T t) where T : IKeyable
|
public bool Exists(IKey key) => StoreHandlerFactory.GetInstance(key.GetKeyType()).Exists(key);
|
||||||
{
|
|
||||||
StoreHandlerFactory.GetInstance(t.GetKey().GetKeyType()).Store(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Exists(IKey t) => StoreHandlerFactory.GetInstance(t.GetKeyType()).Exists(t);
|
public T GetItem<T>(IKey key) where T : IKeyable => StoreHandlerFactory.GetInstance(key.GetKeyType()).Retrieve<T>(key);
|
||||||
|
|
||||||
public T GetItem<T>(IKey key) where T : IKeyable =>
|
|
||||||
StoreHandlerFactory.GetInstance(key.GetKeyType()).Retrieve<T>(key);
|
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
@ -5,27 +5,27 @@ namespace LootDumpProcessor.Storage.Implementations.File.Handlers;
|
|||||||
|
|
||||||
public abstract class AbstractStoreHandler : IStoreHandler
|
public abstract class AbstractStoreHandler : IStoreHandler
|
||||||
{
|
{
|
||||||
public void Store<T>(T obj, bool failIfDuplicate = true) where T : IKeyable
|
public void Store<TEntity>(TEntity entity, bool failIfDuplicate = true) where TEntity : IKeyable
|
||||||
{
|
{
|
||||||
var locationWithFile = GetLocation(obj.GetKey());
|
var locationWithFile = GetLocation(entity.GetKey());
|
||||||
if (System.IO.File.Exists(locationWithFile) && failIfDuplicate)
|
if (System.IO.File.Exists(locationWithFile) && failIfDuplicate)
|
||||||
throw new Exception($"Attempted to save duplicated object into data storage: {locationWithFile}");
|
throw new Exception($"Attempted to save duplicated object into data storage: {locationWithFile}");
|
||||||
|
|
||||||
System.IO.File.WriteAllText(locationWithFile, JsonSerializer.Serialize(obj, JsonSerializerSettings.Default));
|
System.IO.File.WriteAllText(locationWithFile, JsonSerializer.Serialize(entity, JsonSerializerSettings.Default));
|
||||||
}
|
}
|
||||||
|
|
||||||
public T? Retrieve<T>(IKey obj) where T : IKeyable
|
public TEntity? Retrieve<TEntity>(IKey key) where TEntity : IKeyable
|
||||||
{
|
{
|
||||||
var locationWithFile = GetLocation(obj);
|
var locationWithFile = GetLocation(key);
|
||||||
if (!System.IO.File.Exists(locationWithFile)) return default;
|
if (!System.IO.File.Exists(locationWithFile)) return default;
|
||||||
|
|
||||||
return JsonSerializer.Deserialize<T>(System.IO.File.ReadAllText(locationWithFile),
|
return JsonSerializer.Deserialize<TEntity>(System.IO.File.ReadAllText(locationWithFile),
|
||||||
JsonSerializerSettings.Default);
|
JsonSerializerSettings.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Exists(IKey obj)
|
public bool Exists(IKey key)
|
||||||
{
|
{
|
||||||
var locationWithFile = GetLocation(obj);
|
var locationWithFile = GetLocation(key);
|
||||||
return System.IO.File.Exists(locationWithFile);
|
return System.IO.File.Exists(locationWithFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ namespace LootDumpProcessor.Storage.Implementations.File;
|
|||||||
|
|
||||||
public interface IStoreHandler
|
public interface IStoreHandler
|
||||||
{
|
{
|
||||||
void Store<T>(T obj, bool failIfDuplicate = true) where T : IKeyable;
|
void Store<TEntity>(TEntity entity, bool failIfDuplicate = true) where TEntity : IKeyable;
|
||||||
T? Retrieve<T>(IKey obj) where T : IKeyable;
|
TEntity? Retrieve<TEntity>(IKey key) where TEntity : IKeyable;
|
||||||
bool Exists(IKey obj);
|
bool Exists(IKey key);
|
||||||
}
|
}
|
@ -1,28 +1,23 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
using LootDumpProcessor.Storage.Implementations.File.Handlers;
|
using LootDumpProcessor.Storage.Implementations.File.Handlers;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Storage.Implementations.File;
|
namespace LootDumpProcessor.Storage.Implementations.File;
|
||||||
|
|
||||||
public class StoreHandlerFactory
|
public class StoreHandlerFactory
|
||||||
{
|
{
|
||||||
private static Dictionary<KeyType, IStoreHandler> _handlers = new();
|
private static readonly ConcurrentDictionary<KeyType, IStoreHandler> Handlers = new();
|
||||||
private static object lockObject = new();
|
|
||||||
|
|
||||||
public static IStoreHandler GetInstance(KeyType type)
|
public static IStoreHandler GetInstance(KeyType type)
|
||||||
{
|
{
|
||||||
IStoreHandler handler;
|
if (Handlers.TryGetValue(type, out var handler)) return handler;
|
||||||
lock (lockObject)
|
|
||||||
|
handler = type switch
|
||||||
{
|
{
|
||||||
if (!_handlers.TryGetValue(type, out handler))
|
KeyType.Unique => new FlatStoreHandler(),
|
||||||
{
|
KeyType.Subdivisioned => new SubdivisionedStoreHandler(),
|
||||||
handler = type switch
|
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||||
{
|
};
|
||||||
KeyType.Unique => new FlatStoreHandler(),
|
Handlers.TryAdd(type, handler);
|
||||||
KeyType.Subdivisioned => new SubdivisionedStoreHandler(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
|
||||||
};
|
|
||||||
_handlers.Add(type, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,22 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Storage.Implementations.Memory;
|
namespace LootDumpProcessor.Storage.Implementations.Memory;
|
||||||
|
|
||||||
public class MemoryDataStorage : IDataStorage
|
public class MemoryDataStorage : IDataStorage
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, object> CachedObjects = new();
|
private readonly ConcurrentDictionary<string, object> _storage = new();
|
||||||
private static readonly object _cacheObjectLock = new();
|
|
||||||
|
|
||||||
public void Setup()
|
public void Store<TEntity>(TEntity entity) where TEntity : IKeyable =>
|
||||||
|
_storage.TryAdd(GetLookupKey(entity.GetKey()), entity);
|
||||||
|
|
||||||
|
public TEntity? GetItem<TEntity>(IKey key) where TEntity : IKeyable
|
||||||
{
|
{
|
||||||
}
|
if (_storage.TryGetValue(GetLookupKey(key), out var value)) return (TEntity)value;
|
||||||
|
|
||||||
public void Store<T>(T t) where T : IKeyable
|
|
||||||
{
|
|
||||||
lock (_cacheObjectLock)
|
|
||||||
{
|
|
||||||
CachedObjects.Add(GetLookupKey(t.GetKey()), t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Exists(IKey t)
|
|
||||||
{
|
|
||||||
lock (_cacheObjectLock)
|
|
||||||
{
|
|
||||||
return CachedObjects.ContainsKey(GetLookupKey(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? GetItem<T>(IKey key) where T : IKeyable
|
|
||||||
{
|
|
||||||
lock (_cacheObjectLock)
|
|
||||||
{
|
|
||||||
if (CachedObjects.TryGetValue(GetLookupKey(key), out var value)) return (T)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetLookupKey(IKey key) => string.Join("-", key.GetLookupIndex());
|
public bool Exists(IKey key) => _storage.ContainsKey(GetLookupKey(key));
|
||||||
|
|
||||||
public void Clear()
|
private string GetLookupKey(IKey key) => string.Join("-", key.GetLookupIndex());
|
||||||
{
|
public void Clear() => _storage.Clear();
|
||||||
lock (_cacheObjectLock)
|
|
||||||
{
|
|
||||||
CachedObjects.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user