mirror of
https://github.com/sp-tarkov/loot-dump-processor.git
synced 2025-02-12 22:10:45 -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();
|
||||
|
||||
// Setup Data storage
|
||||
DataStorageFactory.GetInstance().Setup();
|
||||
|
||||
// startup the pipeline
|
||||
var pipeline = serviceProvider.GetRequiredService<IPipeline>();
|
||||
await pipeline.Execute();
|
||||
|
@ -2,13 +2,14 @@ namespace LootDumpProcessor.Storage;
|
||||
|
||||
public abstract class AbstractKey(string[] indexes) : IKey
|
||||
{
|
||||
private string[] _indexes = indexes;
|
||||
public abstract KeyType GetKeyType();
|
||||
|
||||
public string SerializedKey
|
||||
{
|
||||
get => string.Join("|", indexes);
|
||||
set => indexes = value.Split("|");
|
||||
get => string.Join("|", _indexes);
|
||||
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.Memory;
|
||||
|
||||
@ -5,9 +6,7 @@ namespace LootDumpProcessor.Storage;
|
||||
|
||||
public static class DataStorageFactory
|
||||
{
|
||||
private static readonly Dictionary<DataStorageTypes, IDataStorage> _dataStorage = new();
|
||||
|
||||
private static object lockObject = new();
|
||||
private static readonly ConcurrentDictionary<DataStorageTypes, IDataStorage> DataStorage = new();
|
||||
|
||||
/**
|
||||
* Requires LootDumpProcessorContext to be initialized before using
|
||||
@ -15,23 +14,18 @@ public static class DataStorageFactory
|
||||
public static IDataStorage GetInstance() =>
|
||||
GetInstance(LootDumpProcessorContext.GetConfig().DataStorageConfig.DataStorageType);
|
||||
|
||||
public static IDataStorage GetInstance(DataStorageTypes type)
|
||||
private static IDataStorage GetInstance(DataStorageTypes type)
|
||||
{
|
||||
IDataStorage dataStorage;
|
||||
lock (lockObject)
|
||||
if (DataStorage.TryGetValue(type, out var dataStorage)) return dataStorage;
|
||||
|
||||
dataStorage = type switch
|
||||
{
|
||||
if (!_dataStorage.TryGetValue(type, out dataStorage))
|
||||
{
|
||||
dataStorage = type switch
|
||||
{
|
||||
DataStorageTypes.File => new FileDataStorage(),
|
||||
DataStorageTypes.Memory => new MemoryDataStorage(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
DataStorageTypes.File => new FileDataStorage(),
|
||||
DataStorageTypes.Memory => new MemoryDataStorage(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
|
||||
_dataStorage.Add(type, dataStorage);
|
||||
}
|
||||
}
|
||||
DataStorage.TryAdd(type, dataStorage);
|
||||
|
||||
return dataStorage;
|
||||
}
|
||||
|
@ -2,9 +2,8 @@ namespace LootDumpProcessor.Storage;
|
||||
|
||||
public interface IDataStorage
|
||||
{
|
||||
void Setup();
|
||||
void Store<T>(T t) where T : IKeyable;
|
||||
bool Exists(IKey t);
|
||||
T GetItem<T>(IKey key) where T : IKeyable;
|
||||
void Store<TEntity>(TEntity entity) where TEntity : IKeyable;
|
||||
TEntity? GetItem<TEntity>(IKey key) where TEntity : IKeyable;
|
||||
bool Exists(IKey key);
|
||||
void Clear();
|
||||
}
|
@ -2,19 +2,14 @@ namespace LootDumpProcessor.Storage.Implementations.File;
|
||||
|
||||
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
|
||||
{
|
||||
StoreHandlerFactory.GetInstance(t.GetKey().GetKeyType()).Store(t);
|
||||
}
|
||||
public bool Exists(IKey key) => StoreHandlerFactory.GetInstance(key.GetKeyType()).Exists(key);
|
||||
|
||||
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()
|
||||
{
|
||||
|
@ -5,27 +5,27 @@ namespace LootDumpProcessor.Storage.Implementations.File.Handlers;
|
||||
|
||||
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)
|
||||
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;
|
||||
|
||||
return JsonSerializer.Deserialize<T>(System.IO.File.ReadAllText(locationWithFile),
|
||||
return JsonSerializer.Deserialize<TEntity>(System.IO.File.ReadAllText(locationWithFile),
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ namespace LootDumpProcessor.Storage.Implementations.File;
|
||||
|
||||
public interface IStoreHandler
|
||||
{
|
||||
void Store<T>(T obj, bool failIfDuplicate = true) where T : IKeyable;
|
||||
T? Retrieve<T>(IKey obj) where T : IKeyable;
|
||||
bool Exists(IKey obj);
|
||||
void Store<TEntity>(TEntity entity, bool failIfDuplicate = true) where TEntity : IKeyable;
|
||||
TEntity? Retrieve<TEntity>(IKey key) where TEntity : IKeyable;
|
||||
bool Exists(IKey key);
|
||||
}
|
@ -1,28 +1,23 @@
|
||||
using System.Collections.Concurrent;
|
||||
using LootDumpProcessor.Storage.Implementations.File.Handlers;
|
||||
|
||||
namespace LootDumpProcessor.Storage.Implementations.File;
|
||||
|
||||
public class StoreHandlerFactory
|
||||
{
|
||||
private static Dictionary<KeyType, IStoreHandler> _handlers = new();
|
||||
private static object lockObject = new();
|
||||
private static readonly ConcurrentDictionary<KeyType, IStoreHandler> Handlers = new();
|
||||
|
||||
public static IStoreHandler GetInstance(KeyType type)
|
||||
{
|
||||
IStoreHandler handler;
|
||||
lock (lockObject)
|
||||
if (Handlers.TryGetValue(type, out var handler)) return handler;
|
||||
|
||||
handler = type switch
|
||||
{
|
||||
if (!_handlers.TryGetValue(type, out handler))
|
||||
{
|
||||
handler = type switch
|
||||
{
|
||||
KeyType.Unique => new FlatStoreHandler(),
|
||||
KeyType.Subdivisioned => new SubdivisionedStoreHandler(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
_handlers.Add(type, handler);
|
||||
}
|
||||
}
|
||||
KeyType.Unique => new FlatStoreHandler(),
|
||||
KeyType.Subdivisioned => new SubdivisionedStoreHandler(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
Handlers.TryAdd(type, handler);
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
@ -1,47 +1,22 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace LootDumpProcessor.Storage.Implementations.Memory;
|
||||
|
||||
public class MemoryDataStorage : IDataStorage
|
||||
{
|
||||
private static readonly Dictionary<string, object> CachedObjects = new();
|
||||
private static readonly object _cacheObjectLock = new();
|
||||
private readonly ConcurrentDictionary<string, object> _storage = 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
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (_storage.TryGetValue(GetLookupKey(key), out var value)) return (TEntity)value;
|
||||
return default;
|
||||
}
|
||||
|
||||
private string GetLookupKey(IKey key) => string.Join("-", key.GetLookupIndex());
|
||||
public bool Exists(IKey key) => _storage.ContainsKey(GetLookupKey(key));
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_cacheObjectLock)
|
||||
{
|
||||
CachedObjects.Clear();
|
||||
}
|
||||
}
|
||||
private string GetLookupKey(IKey key) => string.Join("-", key.GetLookupIndex());
|
||||
public void Clear() => _storage.Clear();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user