Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2944eac2f8 | ||
![]() |
a03246a07d | ||
![]() |
44f6b40e79 | ||
![]() |
9503e7c42d |
65
.gitea/workflows/release.yml
Normal file
65
.gitea/workflows/release.yml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: '9.0.x'
|
||||||
|
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore Firefly.sln
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
chmod +x build-all.sh
|
||||||
|
./build-all.sh
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
name: Firefly v1.0.0 - Initial Release
|
||||||
|
body: |
|
||||||
|
# Firefly v1.0.0 - Initial Release
|
||||||
|
|
||||||
|
We're excited to announce the initial release of Firefly! This version includes the core functionality of our application.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Native library support for enhanced performance
|
||||||
|
- Self-contained executable builds
|
||||||
|
- Cross-platform compatibility (Linux, macOS)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Choose the appropriate build for your platform from the assets below.
|
||||||
|
|
||||||
|
## System Requirements
|
||||||
|
- .NET 9.0 Runtime (for non-self-contained builds)
|
||||||
|
- Linux x64, macOS x64, or Windows x64
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
- None reported
|
||||||
|
|
||||||
|
## What's Next
|
||||||
|
- Additional platform support
|
||||||
|
- Performance optimizations
|
||||||
|
- Enhanced documentation
|
||||||
|
|
||||||
|
## Feedback
|
||||||
|
We welcome your feedback and bug reports. Please use the issue tracker to report any problems.
|
||||||
|
files: |
|
||||||
|
artifacts/exe/*
|
||||||
|
artifacts/native/*
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -227,6 +227,13 @@
|
|||||||
<param name="args">Command arguments containing the key</param>
|
<param name="args">Command arguments containing the key</param>
|
||||||
<returns>The popped value or nil if the list is empty</returns>
|
<returns>The popped value or nil if the list is empty</returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:Firefly.Firefly.HandleLLengthCommand(System.String)">
|
||||||
|
<summary>
|
||||||
|
Handles the LLEN command which returns the length of a list.
|
||||||
|
</summary>
|
||||||
|
<param name="args">Command arguments containing the key</param>
|
||||||
|
<returns>The length of the list or 0 if the key does not exist</returns>
|
||||||
|
</member>
|
||||||
<member name="M:Firefly.Firefly.HandleLRangeCommand(System.String)">
|
<member name="M:Firefly.Firefly.HandleLRangeCommand(System.String)">
|
||||||
<summary>
|
<summary>
|
||||||
Handles the LRANGE command which returns a range of elements from a list.
|
Handles the LRANGE command which returns a range of elements from a list.
|
||||||
@ -313,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.
@ -227,6 +227,13 @@
|
|||||||
<param name="args">Command arguments containing the key</param>
|
<param name="args">Command arguments containing the key</param>
|
||||||
<returns>The popped value or nil if the list is empty</returns>
|
<returns>The popped value or nil if the list is empty</returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:Firefly.Firefly.HandleLLengthCommand(System.String)">
|
||||||
|
<summary>
|
||||||
|
Handles the LLEN command which returns the length of a list.
|
||||||
|
</summary>
|
||||||
|
<param name="args">Command arguments containing the key</param>
|
||||||
|
<returns>The length of the list or 0 if the key does not exist</returns>
|
||||||
|
</member>
|
||||||
<member name="M:Firefly.Firefly.HandleLRangeCommand(System.String)">
|
<member name="M:Firefly.Firefly.HandleLRangeCommand(System.String)">
|
||||||
<summary>
|
<summary>
|
||||||
Handles the LRANGE command which returns a range of elements from a list.
|
Handles the LRANGE command which returns a range of elements from a list.
|
||||||
@ -313,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)
|
||||||
|
@ -188,7 +188,10 @@ namespace Firefly
|
|||||||
|
|
||||||
case "LINDEX":
|
case "LINDEX":
|
||||||
return HandleLIndexCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");
|
return HandleLIndexCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");
|
||||||
|
|
||||||
|
case "LLEN":
|
||||||
|
return HandleLLengthCommand(parts.Length > 1 ? parts[1] : "");
|
||||||
|
|
||||||
case "LRANGE":
|
case "LRANGE":
|
||||||
return HandleLRangeCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");
|
return HandleLRangeCommand(parts.Length > 1 ? string.Join(" ", parts.Skip(1)) : "");
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -152,6 +152,26 @@ namespace Firefly
|
|||||||
: Encoding.UTF8.GetBytes("$-1\r\n");
|
: Encoding.UTF8.GetBytes("$-1\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the LLEN command which returns the length of a list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">Command arguments containing the key</param>
|
||||||
|
/// <returns>The length of the list or 0 if the key does not exist</returns>
|
||||||
|
static byte[] HandleLLengthCommand(string args)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(args))
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetBytes("-ERR wrong number of arguments for 'llen' command\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
string key = args.Trim();
|
||||||
|
|
||||||
|
// Change to use the generic method correctly by returning the length
|
||||||
|
int length = ListStoreWithReadLock(key, list => list.Count);
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetBytes($":{length}\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the LRANGE command which returns a range of elements from a list.
|
/// Handles the LRANGE command which returns a range of elements from a list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -528,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, _ => []);
|
||||||
@ -567,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
|
||||||
{
|
{
|
||||||
@ -588,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