feat: Implement automatic backup on data modification
Some checks failed
Build / build (push) Failing after 1m20s
Some checks failed
Build / build (push) Failing after 1m20s
Added automatic backup system that triggers saves when data is modified. Changes include: - Added tracking of data modifications in BackupSystem.cs - Added debounce timer (5s) to prevent excessive backups during rapid changes - Modified string, list, and hash operations to track data changes - Ensures data is saved shortly after modifications without impacting performance
This commit is contained in:
parent
a03246a07d
commit
2944eac2f8
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -320,6 +320,11 @@
|
|||||||
<param name="key">The key to execute the action on</param>
|
<param name="key">The key to execute the action on</param>
|
||||||
<param name="action">The action to execute</param>
|
<param name="action">The action to execute</param>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:Firefly.Firefly.DisplayFireflyArt">
|
||||||
|
<summary>
|
||||||
|
Displays Firefly ASCII art in the console
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:Firefly.Firefly.HandleAuthCommand(System.String,System.String)">
|
<member name="M:Firefly.Firefly.HandleAuthCommand(System.String,System.String)">
|
||||||
<summary>
|
<summary>
|
||||||
Handles the AUTH command which authenticates a client.
|
Handles the AUTH command which authenticates a client.
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -320,6 +320,11 @@
|
|||||||
<param name="key">The key to execute the action on</param>
|
<param name="key">The key to execute the action on</param>
|
||||||
<param name="action">The action to execute</param>
|
<param name="action">The action to execute</param>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:Firefly.Firefly.DisplayFireflyArt">
|
||||||
|
<summary>
|
||||||
|
Displays Firefly ASCII art in the console
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="M:Firefly.Firefly.HandleAuthCommand(System.String,System.String)">
|
<member name="M:Firefly.Firefly.HandleAuthCommand(System.String,System.String)">
|
||||||
<summary>
|
<summary>
|
||||||
Handles the AUTH command which authenticates a client.
|
Handles the AUTH command which authenticates a client.
|
||||||
|
@ -8,6 +8,13 @@ namespace Firefly
|
|||||||
public partial class Firefly
|
public partial class Firefly
|
||||||
{
|
{
|
||||||
#region Backup System
|
#region Backup System
|
||||||
|
// Track if data has been modified since last backup
|
||||||
|
private static volatile bool dataModified = false;
|
||||||
|
// Debounce period in milliseconds for data modification backups
|
||||||
|
private static readonly int dataModificationBackupDelayMs = 5000;
|
||||||
|
// Timer for debouncing data modification backups
|
||||||
|
private static System.Timers.Timer? modificationBackupTimer;
|
||||||
|
|
||||||
static void InitializeBackupSystem()
|
static void InitializeBackupSystem()
|
||||||
{
|
{
|
||||||
// Create backup directory if it doesn't exist
|
// Create backup directory if it doesn't exist
|
||||||
@ -33,7 +40,21 @@ namespace Firefly
|
|||||||
backupTimer.AutoReset = true;
|
backupTimer.AutoReset = true;
|
||||||
backupTimer.Start();
|
backupTimer.Start();
|
||||||
|
|
||||||
|
// Set up data modification backup timer
|
||||||
|
modificationBackupTimer = new System.Timers.Timer(dataModificationBackupDelayMs);
|
||||||
|
modificationBackupTimer.Elapsed += (sender, e) =>
|
||||||
|
{
|
||||||
|
if (dataModified)
|
||||||
|
{
|
||||||
|
BackupData();
|
||||||
|
dataModified = false;
|
||||||
|
}
|
||||||
|
modificationBackupTimer.Stop(); // Stop timer after backup
|
||||||
|
};
|
||||||
|
modificationBackupTimer.AutoReset = false;
|
||||||
|
|
||||||
Console.WriteLine($"Automatic backup system initialized (every {autoBackupIntervalMinutes} minutes)");
|
Console.WriteLine($"Automatic backup system initialized (every {autoBackupIntervalMinutes} minutes)");
|
||||||
|
Console.WriteLine($"Data modification backup system initialized (after {dataModificationBackupDelayMs/1000} seconds of inactivity)");
|
||||||
Console.WriteLine($"Keeping up to {maxBackupFiles} backup files");
|
Console.WriteLine($"Keeping up to {maxBackupFiles} backup files");
|
||||||
|
|
||||||
// Register backup on exit
|
// Register backup on exit
|
||||||
@ -47,6 +68,22 @@ namespace Firefly
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call this method whenever data is modified
|
||||||
|
internal static void MarkDataAsModified()
|
||||||
|
{
|
||||||
|
if (!backupsEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dataModified = true;
|
||||||
|
|
||||||
|
// Reset and start the timer to debounce multiple rapid changes
|
||||||
|
if (modificationBackupTimer != null)
|
||||||
|
{
|
||||||
|
modificationBackupTimer.Stop();
|
||||||
|
modificationBackupTimer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void LoadDataFromBackup()
|
static void LoadDataFromBackup()
|
||||||
{
|
{
|
||||||
if (!backupsEnabled)
|
if (!backupsEnabled)
|
||||||
|
@ -27,10 +27,11 @@ namespace Firefly
|
|||||||
{
|
{
|
||||||
var hash = GetOrCreateHash(key);
|
var hash = GetOrCreateHash(key);
|
||||||
bool isNewField = hash.TryAdd(field, value);
|
bool isNewField = hash.TryAdd(field, value);
|
||||||
|
|
||||||
if (!isNewField)
|
if (!isNewField)
|
||||||
{
|
|
||||||
hash[field] = value;
|
hash[field] = value;
|
||||||
}
|
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
return Encoding.UTF8.GetBytes($":{(isNewField ? 1 : 0)}\r\n");
|
return Encoding.UTF8.GetBytes($":{(isNewField ? 1 : 0)}\r\n");
|
||||||
}
|
}
|
||||||
@ -91,10 +92,11 @@ namespace Firefly
|
|||||||
{
|
{
|
||||||
bool removed = hash.TryRemove(field, out _);
|
bool removed = hash.TryRemove(field, out _);
|
||||||
|
|
||||||
|
if (removed)
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
if (hash.IsEmpty)
|
if (hash.IsEmpty)
|
||||||
{
|
|
||||||
HashStoreRemove(key);
|
HashStoreRemove(key);
|
||||||
}
|
|
||||||
|
|
||||||
return Encoding.UTF8.GetBytes($":{(removed ? 1 : 0)}\r\n");
|
return Encoding.UTF8.GetBytes($":{(removed ? 1 : 0)}\r\n");
|
||||||
}
|
}
|
||||||
@ -197,6 +199,8 @@ namespace Firefly
|
|||||||
return Encoding.UTF8.GetBytes($"-ERR internal error: {ex.Message}\r\n");
|
return Encoding.UTF8.GetBytes($"-ERR internal error: {ex.Message}\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
return Encoding.UTF8.GetBytes("+OK\r\n");
|
return Encoding.UTF8.GetBytes("+OK\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +224,12 @@ namespace Firefly
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashStoreShards[shardIndex].GetOrAdd(key, _ => new ConcurrentDictionary<string, string>());
|
var hash = hashStoreShards[shardIndex].GetOrAdd(key, _ => new ConcurrentDictionary<string, string>());
|
||||||
|
|
||||||
|
if (hash.IsEmpty)
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -254,7 +263,12 @@ namespace Firefly
|
|||||||
private static bool HashStoreRemove(string key)
|
private static bool HashStoreRemove(string key)
|
||||||
{
|
{
|
||||||
int shardIndex = GetShardIndex(key);
|
int shardIndex = GetShardIndex(key);
|
||||||
return hashStoreShards[shardIndex].TryRemove(key, out _);
|
bool result = hashStoreShards[shardIndex].TryRemove(key, out _);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -548,6 +548,13 @@ namespace Firefly
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Key '{key}' already exists with a different type");
|
throw new InvalidOperationException($"Key '{key}' already exists with a different type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var newList = new List<string>();
|
||||||
|
if (listStoreShards[shardIndex].TryAdd(key, newList))
|
||||||
|
{
|
||||||
|
MarkDataAsModified();
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return listStoreShards[shardIndex].GetOrAdd(key, _ => []);
|
return listStoreShards[shardIndex].GetOrAdd(key, _ => []);
|
||||||
@ -587,7 +594,12 @@ namespace Firefly
|
|||||||
listStoreLocks[shardIndex].EnterWriteLock();
|
listStoreLocks[shardIndex].EnterWriteLock();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return listStoreShards[shardIndex].TryRemove(key, out _);
|
bool result = listStoreShards[shardIndex].TryRemove(key, out _);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -608,6 +620,8 @@ namespace Firefly
|
|||||||
{
|
{
|
||||||
var list = GetOrCreateList(key);
|
var list = GetOrCreateList(key);
|
||||||
action(list);
|
action(list);
|
||||||
|
|
||||||
|
MarkDataAsModified();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -9,6 +9,9 @@ namespace Firefly
|
|||||||
#region Server Management
|
#region Server Management
|
||||||
static async Task StartServerAsync()
|
static async Task StartServerAsync()
|
||||||
{
|
{
|
||||||
|
// Display Firefly ASCII art
|
||||||
|
DisplayFireflyArt();
|
||||||
|
|
||||||
// Set up server on specified port
|
// Set up server on specified port
|
||||||
var listener = new TcpListener(IPAddress.Parse(bindAddress), serverPort);
|
var listener = new TcpListener(IPAddress.Parse(bindAddress), serverPort);
|
||||||
listener.Start();
|
listener.Start();
|
||||||
@ -305,6 +308,38 @@ namespace Firefly
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays Firefly ASCII art in the console
|
||||||
|
/// </summary>
|
||||||
|
static void DisplayFireflyArt()
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
Console.WriteLine(
|
||||||
|
" ,,_\n" +
|
||||||
|
" zd$$??=\n" +
|
||||||
|
" z$$P? F:`c, _\n" +
|
||||||
|
" d$$, `c'cc$$i ,cd$?R\n" +
|
||||||
|
" $$$$ cud$,?$$$i ,=P'2?z '\n" +
|
||||||
|
" $` ` ?$$$,?$$$. ,-''`>, bzP\n" +
|
||||||
|
" 'cLdb,?$$,?$$$ ,h' 'I$'J$P\n" +
|
||||||
|
" ... `?$$$,`$$,`$$h $$PxrF'd$'\n" +
|
||||||
|
" d$PP``?-,`?$$,?$h`$$,,$$'$F44'\n" +
|
||||||
|
" ?,,_`=4c,?=,`?hu?$`?L4$'? '\n" +
|
||||||
|
" ```?==``=-`` ```-`'_,,,,\n" +
|
||||||
|
" .ccu?m?e?JC,-,\"=?\n" +
|
||||||
|
" ```=='?'\n");
|
||||||
|
|
||||||
|
Console.WriteLine(@"
|
||||||
|
███████╗██╗██████╗ ███████╗███████╗██╗ ██╗ ██╗
|
||||||
|
██╔════╝██║██╔══██╗██╔════╝██╔════╝██║ ╚██╗ ██╔╝
|
||||||
|
█████╗ ██║██████╔╝█████╗ █████╗ ██║ ╚████╔╝
|
||||||
|
██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║ ╚██╔╝
|
||||||
|
██║ ██║██║ ██║███████╗██║ ███████╗██║
|
||||||
|
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚═╝
|
||||||
|
");
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -88,6 +88,9 @@ namespace Firefly
|
|||||||
|
|
||||||
int shardIndex = GetShardIndex(key);
|
int shardIndex = GetShardIndex(key);
|
||||||
stringStoreShards[shardIndex][key] = value;
|
stringStoreShards[shardIndex][key] = value;
|
||||||
|
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +114,12 @@ namespace Firefly
|
|||||||
private static bool StringStoreRemove(string key)
|
private static bool StringStoreRemove(string key)
|
||||||
{
|
{
|
||||||
int shardIndex = GetShardIndex(key);
|
int shardIndex = GetShardIndex(key);
|
||||||
return stringStoreShards[shardIndex].TryRemove(key, out _);
|
bool result = stringStoreShards[shardIndex].TryRemove(key, out _);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
MarkDataAsModified();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
#endregion
|
#endregion
|
||||||
|
Loading…
x
Reference in New Issue
Block a user