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="action">The action to execute</param>
|
||||
</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)">
|
||||
<summary>
|
||||
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="action">The action to execute</param>
|
||||
</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)">
|
||||
<summary>
|
||||
Handles the AUTH command which authenticates a client.
|
||||
|
@ -8,6 +8,13 @@ namespace Firefly
|
||||
public partial class Firefly
|
||||
{
|
||||
#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()
|
||||
{
|
||||
// Create backup directory if it doesn't exist
|
||||
@ -33,7 +40,21 @@ namespace Firefly
|
||||
backupTimer.AutoReset = true;
|
||||
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($"Data modification backup system initialized (after {dataModificationBackupDelayMs/1000} seconds of inactivity)");
|
||||
Console.WriteLine($"Keeping up to {maxBackupFiles} backup files");
|
||||
|
||||
// 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()
|
||||
{
|
||||
if (!backupsEnabled)
|
||||
|
@ -27,10 +27,11 @@ namespace Firefly
|
||||
{
|
||||
var hash = GetOrCreateHash(key);
|
||||
bool isNewField = hash.TryAdd(field, value);
|
||||
|
||||
if (!isNewField)
|
||||
{
|
||||
hash[field] = value;
|
||||
}
|
||||
|
||||
MarkDataAsModified();
|
||||
|
||||
return Encoding.UTF8.GetBytes($":{(isNewField ? 1 : 0)}\r\n");
|
||||
}
|
||||
@ -91,10 +92,11 @@ namespace Firefly
|
||||
{
|
||||
bool removed = hash.TryRemove(field, out _);
|
||||
|
||||
if (removed)
|
||||
MarkDataAsModified();
|
||||
|
||||
if (hash.IsEmpty)
|
||||
{
|
||||
HashStoreRemove(key);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
MarkDataAsModified();
|
||||
|
||||
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>
|
||||
@ -254,7 +263,12 @@ namespace Firefly
|
||||
private static bool HashStoreRemove(string 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
|
||||
|
@ -548,6 +548,13 @@ namespace Firefly
|
||||
{
|
||||
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, _ => []);
|
||||
@ -587,7 +594,12 @@ namespace Firefly
|
||||
listStoreLocks[shardIndex].EnterWriteLock();
|
||||
try
|
||||
{
|
||||
return listStoreShards[shardIndex].TryRemove(key, out _);
|
||||
bool result = listStoreShards[shardIndex].TryRemove(key, out _);
|
||||
|
||||
if (result)
|
||||
MarkDataAsModified();
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -608,6 +620,8 @@ namespace Firefly
|
||||
{
|
||||
var list = GetOrCreateList(key);
|
||||
action(list);
|
||||
|
||||
MarkDataAsModified();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -9,6 +9,9 @@ namespace Firefly
|
||||
#region Server Management
|
||||
static async Task StartServerAsync()
|
||||
{
|
||||
// Display Firefly ASCII art
|
||||
DisplayFireflyArt();
|
||||
|
||||
// Set up server on specified port
|
||||
var listener = new TcpListener(IPAddress.Parse(bindAddress), serverPort);
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -88,6 +88,9 @@ namespace Firefly
|
||||
|
||||
int shardIndex = GetShardIndex(key);
|
||||
stringStoreShards[shardIndex][key] = value;
|
||||
|
||||
MarkDataAsModified();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -111,7 +114,12 @@ namespace Firefly
|
||||
private static bool StringStoreRemove(string 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user