Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ajoute la persistance, des commandes et des fichiers Docker #21

Merged
merged 14 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**/bin
**/obj
README.md
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.vs
**/.vscode
**/.settings
**/.toolstarget
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/secrets.dev.yaml
**/values.dev.yaml
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1

FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build

COPY . /source

WORKDIR /source/src/Server

ARG TARGETARCH

RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app

FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final
WORKDIR /app

COPY --from=build /app .

USER $APP_UID

ENTRYPOINT ["dotnet", "Server.dll"]
7 changes: 7 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
services:
server:
build:
context: .
target: final
ports:
- 6379:6379
20 changes: 20 additions & 0 deletions src/Server/ArgParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
public class ArgParser
{
public int Port { get; private set; } = 6379;
public string? Path { get; private set; } = null;
public int SaveInterval { get; private set; } = 5 * 60_000;

public ArgParser(string[] args)
{
List<string> arguments = new(args);

int port = arguments.IndexOf("--port");
if (port >= 0) Port = int.Parse(arguments[port + 1]);

int path = arguments.IndexOf("--path");
if (path >= 0) Path = arguments[path + 1];

int save = arguments.IndexOf("--save-interval");
if (save >= 0) SaveInterval = int.Parse(arguments[save + 1]);
}
}
24 changes: 4 additions & 20 deletions src/Server/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,21 +135,11 @@ public override Item execute(params string[] args)

_db.Lock();
var mut = _db.Get(args[0]);
if (mut == null || mut == "f")
_db.Set(args[0], "l");
if (mut == null) _db.Set(args[0], "locked");
_db.Unlock();

switch (mut)
{
case null:
return new SimpleString("OK");
case "f":
return new SimpleString("OK");
case "l":
return new SimpleError("Key is already locked");
default:
return new SimpleError("Key is already set");
}
if (mut == null) return new SimpleString("OK");
return new SimpleError("Key is already locked");
}
}

Expand All @@ -162,13 +152,7 @@ public override Item execute(params string[] args)
if (args.Length != 1)
return new SimpleError("Expected 1 argument");

_db.Lock();
var mut = _db.Get(args[0]);
if (mut == "l") _db.Set(args[0], "f");
_db.Unlock();

if (mut != "l" && mut != "f")
return new SimpleError("Key is not a lock");
_db.Del(args[0]);

return new SimpleString("OK");
}
Expand Down
76 changes: 76 additions & 0 deletions src/Server/Database.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using Shared.Resp;

public class Database : IDisposable
{
Expand All @@ -7,6 +8,7 @@ public class Database : IDisposable
private readonly object _lock = new();
private readonly object _cleanLock = new();
private CancellationTokenSource _cts = new();
private Timer? _timer;

public Database()
{
Expand Down Expand Up @@ -76,12 +78,86 @@ private void Clean()
}
}

public Item Encode()
{
var db = new Dictionary<Item, Item>();
var data = new Dictionary<Item, Item>();
var ex = new Dictionary<Item, Item>();

lock (_lock)
{
foreach (var (key, value) in _data)
data[new BulkString(key)] = new BulkString(value);

foreach (var (key, value) in _ex)
ex[new BulkString(key)] = new Integer(value.ToUnixTimeMilliseconds());
}

db[new BulkString("data")] = new Map(data);
db[new BulkString("ex")] = new Map(ex);
return new Map(db);
}

public static Database Decode(Item item)
{
if (item is not Map map)
throw new ArgumentException("Expected map");

var dict = new Dictionary<string, Item>();
foreach (var (key, value) in map.Items)
dict[key.ToString() ?? ""] = value;

if (!dict.TryGetValue("data", out var data) || data is not Map dataMap)
throw new ArgumentException("Expected data map");

if (!dict.TryGetValue("ex", out var ex) || ex is not Map exMap)
throw new ArgumentException("Expected ex map");

var db = new Database();
db.Lock();

foreach (var (key, value) in dataMap.Items)
db._data[key.ToString() ?? ""] = value.ToString() ?? "";

foreach (var (key, value) in exMap.Items)
{
if (value is not Integer i) throw new ArgumentException("Expected integer");
db._ex[key.ToString() ?? ""] = DateTimeOffset.FromUnixTimeMilliseconds(i.Value);
}

db.Unlock();
return db;
}

public void Save(string path)
{
File.WriteAllText(path + ".tmp", Encode().Encode());
File.Create(path).Close();
File.Replace(path + ".tmp", path, null);
}

public static Database Load(string path)
{
using var file = File.OpenRead(path);
using var reader = new StreamReader(file);
return Decode(Item.Decode(reader));
}

public static Database Link(string? path, int saveInterval)
{
if (path == null) return new Database();
var db = File.Exists(path) ? Load(path) : new Database();
db._timer = new Timer(_ => db.Save(path), null, 0, saveInterval);
return db;
}

public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
_data.Clear();
_ex.Clear();
_timer?.Dispose();
GC.SuppressFinalize(this);
}
}
7 changes: 3 additions & 4 deletions src/Server/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
int port = 6379;
var arguments = new ArgParser(args);

if (args.Length > 0) port = int.Parse(args[0]);
var database = Database.Link(arguments.Path, arguments.SaveInterval);
var server = new Server(arguments.Port, database);

var database = new Database();
var server = new Server(port, database);
server.Run();
28 changes: 28 additions & 0 deletions tests/Server.UnitTests/Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,32 @@ public void AutoClean()

Assert.Equal(1, data?.Count);
}

[Fact]
public void EncodeDecode()
{
using var db1 = new Database();
db1.Set("key1", "value1");
db1.Set("key2", "value2", 500);

using var db2 = Database.Decode(db1.Encode());

Assert.Equal(db1.Encode().ToString(), db2.Encode().ToString());
}

[Fact]
public void SaveLoad()
{
using var db1 = new Database();
db1.Set("key1", "value1");
db1.Set("key2", "value2", 500);

db1.Save("db.dat");

using var db2 = Database.Load("db.dat");

Assert.Equal(db1.Encode().ToString(), db2.Encode().ToString());

File.Delete("db.dat");
}
}