-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathProgram.cs
More file actions
143 lines (115 loc) · 5.24 KB
/
Copy pathProgram.cs
File metadata and controls
143 lines (115 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using EntglDb.Core;
using EntglDb.Core.Storage;
using EntglDb.Core.Cache;
using EntglDb.Core.Sync;
using EntglDb.Core.Diagnostics;
using EntglDb.Core.Resilience;
using EntglDb.Network;
using EntglDb.Network.Security;
using EntglDb.Persistence.BLite;
using EntglDb.Sample.Shared;
using Microsoft.Extensions.Hosting;
using EntglDb.Core.Network;
using EntglDb.Sync;
namespace EntglDb.Sample.Console;
// Local User/Address classes removed in favor of Shared project
class Program
{
static async Task Main(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
// Configuration
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
// Logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Information);
var randomPort = new Random().Next(1000, 9999);
// Node ID
string nodeId = args.Length > 0 ? args[0] : ("node-" + randomPort);
int tcpPort = args.Length > 1 ? int.Parse(args[1]) : randomPort;
// Conflict Resolution Strategy (can be switched at runtime via service replacement)
var useRecursiveMerge = args.Contains("--merge");
if (useRecursiveMerge)
{
builder.Services.AddSingleton<IConflictResolver, RecursiveNodeMergeConflictResolver>();
}
IPeerNodeConfigurationProvider peerNodeConfigurationProvider = new StaticPeerNodeConfigurationProvider(
new PeerNodeConfiguration
{
NodeId = nodeId,
TcpPort = tcpPort,
AuthToken = "Test-Cluster-Key",
//KnownPeers = builder.Configuration.GetSection("EntglDb:KnownPeers").Get<List<KnownPeerConfiguration>>() ?? new()
});
builder.Services.AddSingleton<IPeerNodeConfigurationProvider>(peerNodeConfigurationProvider);
// Database path
var dataPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data");
Directory.CreateDirectory(dataPath);
var databasePath = Path.Combine(dataPath, $"{nodeId}.blite");
// Register EntglDb Services using Fluent Extensions with BLite, SampleDbContext, and SampleDocumentStore
builder.Services.AddEntglDbCore()
.AddEntglDbBLite<SampleDbContext, SampleDocumentStore>(sp => new SampleDbContext(databasePath), databasePath + ".meta")
.AddEntglDbNetwork<StaticPeerNodeConfigurationProvider>() // transport only
.AddEntglDbSync(); // sync handlers + node orchestrator
builder.Services.AddHostedService<ConsoleInteractiveService>(); // Runs the Input Loop
var host = builder.Build();
System.Console.WriteLine($"? Node {nodeId} initialized on port {tcpPort}");
System.Console.WriteLine($"? Database: {databasePath}");
System.Console.WriteLine();
await host.RunAsync();
}
private class StaticPeerNodeConfigurationProvider : IPeerNodeConfigurationProvider
{
public PeerNodeConfiguration Configuration { get; set; }
public StaticPeerNodeConfigurationProvider(PeerNodeConfiguration configuration)
{
Configuration = configuration;
}
public event PeerNodeConfigurationChangedEventHandler? ConfigurationChanged;
public Task<PeerNodeConfiguration> GetConfiguration()
{
return Task.FromResult(Configuration);
}
protected virtual void OnConfigurationChanged(PeerNodeConfiguration newConfig)
{
ConfigurationChanged?.Invoke(this, newConfig);
}
}
public class SimpleFileLoggerProvider : ILoggerProvider
{
private readonly string _path;
public SimpleFileLoggerProvider(string path) => _path = path;
public ILogger CreateLogger(string categoryName) => new SimpleFileLogger(categoryName, _path);
public void Dispose() { }
}
public class SimpleFileLogger : ILogger
{
private readonly string _category;
private readonly string _path;
private static object _lock = new object();
public SimpleFileLogger(string category, string path)
{
_category = category;
_path = path;
}
public IDisposable BeginScope<TState>(TState state) where TState : notnull => null!;
public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Warning;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel)) return;
var msg = $"{DateTime.Now:O} [{logLevel}] {_category}: {formatter(state, exception)}";
if (exception != null) msg += $"\n{exception}";
// Simple append, no retry needed for unique files
try
{
File.AppendAllText(_path, msg + Environment.NewLine);
}
catch { /* Ignore logging errors */ }
}
}
}