You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel.SqLite/SqLiteDupStorage.cs

183 lines
5.7 KiB

5 years ago
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
namespace ZeroLevel.SqLite
5 years ago
{
/// <summary>
/// Хранит данные указанное число дней, и позволяет выполнить проверку наличия данных, для отбрасывания дубликатов
/// </summary>
public sealed class SqLiteDupStorage
: BaseSqLiteDB, IDisposable
{
#region Fields
private const string DEFAUL_TABLE_NAME = "History";
private readonly SQLiteConnection _db;
private readonly long _removeOldRecordsTaskKey;
private readonly int _countDays;
private readonly string _table_name;
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
#endregion Fields
#region Private members
private sealed class DuplicationStorageRecord
{
public string Hash { get; set; }
public byte[] Body { get; set; }
public long Timestamp { get; set; }
}
private void RemoveOldRecordsTask(long key)
{
_rwLock.EnterWriteLock();
try
{
Execute($"DELETE FROM {_table_name} WHERE timestamp < @limit", _db,
new SQLiteParameter[] { new SQLiteParameter("limit", DateTime.Now.AddDays(-_countDays).Ticks) });
}
catch (Exception ex)
{
Log.Error(ex, "[SQLiteDupStorage] Fault remove old records from db");
}
finally
{
_rwLock.ExitWriteLock();
}
}
#endregion Private members
#region Ctor
public SqLiteDupStorage(string database_file_path, string tableName, int period)
{
var path = PrepareDb(database_file_path);
if (string.IsNullOrWhiteSpace(tableName))
{
_table_name = DEFAUL_TABLE_NAME;
}
else
{
_table_name = tableName;
}
_db = new SQLiteConnection($"Data Source={path};Version=3;");
_db.Open();
Execute($"CREATE TABLE IF NOT EXISTS {_table_name} (id INTEGER PRIMARY KEY AUTOINCREMENT, hash TEXT, body BLOB, timestamp INTEGER)", _db);
Execute($"CREATE INDEX IF NOT EXISTS hash_index ON {_table_name} (hash)", _db);
_countDays = period > 0 ? period : 1;
_removeOldRecordsTaskKey = Sheduller.RemindEvery(TimeSpan.FromMinutes(1), RemoveOldRecordsTask);
}
#endregion Ctor
#region API
/// <summary>
/// true в случае обнаружения дубликата
/// </summary>
public bool TestAndInsert(byte[] body)
{
var hash = GenerateSHA256String(body);
var timestamp = DateTime.Now.Ticks;
SQLiteDataReader reader;
_rwLock.EnterReadLock();
var exists = new List<byte[]>();
try
{
reader = Read($"SELECT body FROM {_table_name} WHERE hash=@hash", _db, new SQLiteParameter[] { new SQLiteParameter("hash", hash) });
while (reader.Read())
{
exists.Add((byte[])reader.GetValue(0));
}
reader.Close();
}
catch (Exception ex)
{
Log.Error(ex, $"[SQLiteDupStorage] Fault search existing records by hash ({hash})");
// no return!
}
finally
{
_rwLock.ExitReadLock();
}
reader = null;
if (exists.Any())
{
foreach (var candidate in exists)
{
if (ArrayExtensions.UnsafeEquals(candidate, body))
return true;
}
}
_rwLock.EnterWriteLock();
try
{
Execute($"INSERT INTO {_table_name} ('hash', 'body', 'timestamp') values (@hash, @body, @timestamp)", _db,
new SQLiteParameter[]
{
new SQLiteParameter("hash", hash),
new SQLiteParameter("body", body),
new SQLiteParameter("timestamp", timestamp)
});
}
catch (Exception ex)
{
Log.Error(ex, $"[SQLiteDupStorage] Fault insert record in duplications storage. Hash '{hash}'. Timestamp '{timestamp}'.");
}
finally
{
_rwLock.ExitWriteLock();
}
return false;
}
#endregion API
#region IDisposable
public void Dispose()
{
Sheduller.Remove(_removeOldRecordsTaskKey);
try
{
_db?.Dispose();
}
catch (Exception ex)
{
Log.Error(ex, "[SQLiteDupStorage] Fault close db connection");
}
}
#endregion IDisposable
#region Helpers
private static string GenerateSHA256String(byte[] bytes)
{
using (SHA256 sha256 = SHA256Managed.Create())
{
byte[] hash = sha256.ComputeHash(bytes);
return ByteArrayToString(hash);
}
}
private static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
#endregion Helpers
}
}

Powered by TurnKey Linux.