Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
StorageService.cs
1using System.Text;
2using Waher.Events;
8
10{
11 [Singleton]
12 internal sealed class StorageService : IStorageService, IDisposable
13 {
14 private readonly LinkedList<TaskCompletionSource<bool>> tasksWaiting = new();
15 private readonly string dataFolder;
16 private FilesProvider? databaseProvider;
17 private PersistedEventLog? persistedEventLog;
18 private bool? initialized = null;
19 private bool started = false;
20
24 public StorageService()
25 {
26 string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
27 this.dataFolder = Path.Combine(appDataFolder, "Data");
28 }
29
33 public string DataFolder => this.dataFolder;
34
35 #region LifeCycle management
36
38 public async Task Init(CancellationToken? cancellationToken)
39 {
40 lock (this.tasksWaiting)
41 {
42 if (this.started)
43 return;
44
45 this.started = true;
46 }
47
48 try
49 {
51 this.databaseProvider = Database.Provider as FilesProvider;
52
53 if (this.databaseProvider is null)
54 {
55 this.databaseProvider = await this.CreateDatabaseFile();
56
57 await this.databaseProvider.RepairIfInproperShutdown(string.Empty);
58 await this.databaseProvider.Start();
59 }
60
61 if (this.databaseProvider is not null)
62 {
63 Database.Register(this.databaseProvider, false);
64 Log.Register(this.persistedEventLog = new PersistedEventLog(90));
65 this.InitDone(true);
66 return;
67 }
68 }
69 catch (Exception e1)
70 {
71 e1 = Log.UnnestException(e1);
72 ServiceRef.LogService.LogException(e1);
73 }
74
76 /* On iOS the UI is not initialized at this point, need to find another solution
77 if (await ServiceRef.UiSerializer.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.DatabaseIssue"], ServiceRef.Localizer[nameof(AppResources.DatabaseCorruptInfoText"], ServiceRef.Localizer[nameof(AppResources.RepairAndContinue"], ServiceRef.Localizer[nameof(AppResources.ContinueAnyway"]))
78 */
79 //TODO: when UI is ready, show an alert that the database was reset due to unrecoverable error
80 //TODO: say to close the application in a controlled manner
81 {
82 try
83 {
84 Directory.Delete(this.dataFolder, true);
85
86 this.databaseProvider = await this.CreateDatabaseFile();
87
88 await this.databaseProvider.RepairIfInproperShutdown(string.Empty);
89
90 await this.databaseProvider.Start();
91
93 {
94 Database.Register(this.databaseProvider, false);
95 Log.Register(this.persistedEventLog = new PersistedEventLog(90));
96 this.InitDone(true);
97 return;
98 }
99 }
100 catch (Exception e3)
101 {
102 e3 = Log.UnnestException(e3);
103 ServiceRef.LogService.LogException(e3);
104
105 await App.Stop();
106 /*
107 Thread?.NewState("UI");
108 await ServiceRef.UiSerializer.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.DatabaseIssue"], ServiceRef.Localizer[nameof(AppResources.DatabaseRepairFailedInfoText"], ServiceRef.Localizer[nameof(AppResources.Ok"]);
109 */
110 }
111 }
112
113 this.InitDone(false);
114 }
115
116 private void InitDone(bool Result)
117 {
118 lock (this.tasksWaiting)
119 {
120 this.initialized = Result;
121
122 foreach (TaskCompletionSource<bool> Wait in this.tasksWaiting)
123 Wait.TrySetResult(Result);
124
125 this.tasksWaiting.Clear();
126 }
127 }
128
130 public Task<bool> WaitInitDone()
131 {
132 lock (this.tasksWaiting)
133 {
134 if (this.initialized.HasValue)
135 return Task.FromResult<bool>(this.initialized.Value);
136
137 TaskCompletionSource<bool> Wait = new();
138 this.tasksWaiting.AddLast(Wait);
139
140 return Wait.Task;
141 }
142 }
143
145 public async Task Shutdown()
146 {
147 lock (this.tasksWaiting)
148 {
149 this.initialized = null;
150 this.started = false;
151 }
152
153 try
154 {
155 if (this.persistedEventLog is not null)
156 {
157 Log.Unregister(this.persistedEventLog);
158 this.persistedEventLog.Dispose();
159 this.persistedEventLog = null;
160 }
161
162 if (this.databaseProvider is not null)
163 {
165 await this.databaseProvider.Flush();
166 await this.databaseProvider.Stop();
167 this.databaseProvider = null;
168 }
169 }
170 catch (Exception ex)
171 {
172 ServiceRef.LogService.LogException(ex);
173 }
174 }
175
176 private Task<FilesProvider> CreateDatabaseFile()
177 {
178 FilesProvider.AsyncFileIo = false; // Asynchronous file I/O induces a long delay during startup on mobile platforms. Why??
179
180 return FilesProvider.CreateAsync(this.dataFolder, "Default", 8192, 10000, 8192, Encoding.UTF8,
181 (int)Constants.Timeouts.Database.TotalMilliseconds, ServiceRef.CryptoService.GetCustomKey);
182 }
183
187 public void Dispose()
188 {
189 this.persistedEventLog?.Dispose();
190 this.persistedEventLog = null;
191
192 this.databaseProvider?.Dispose();
193 this.databaseProvider = null;
194 }
195
196 #endregion
197
198 public async Task Insert(object obj)
199 {
200 await Database.Insert(obj);
201 await Database.Provider.Flush();
202 }
203
204 public async Task Update(object obj)
205 {
206 await Database.Update(obj);
207 await Database.Provider.Flush();
208 }
209
210 public Task<T> FindFirstDeleteRest<T>() where T : class
211 {
212 return Database.FindFirstDeleteRest<T>();
213 }
214
215 public Task<T> FindFirstIgnoreRest<T>() where T : class
216 {
217 return Database.FindFirstIgnoreRest<T>();
218 }
219
220 public Task Export(IDatabaseExport exportOutput)
221 {
222 return Database.Export(exportOutput);
223 }
224
228 public void FlagForRepair()
229 {
230 this.DeleteFile("Start.txt");
231 this.DeleteFile("Stop.txt");
232 }
233
234 private void DeleteFile(string FileName)
235 {
236 try
237 {
238 FileName = Path.Combine(this.dataFolder, FileName);
239
240 if (File.Exists(FileName))
241 File.Delete(FileName);
242 }
243 catch (Exception)
244 {
245 // Ignore, to avoid infinite loops if event log has an inconsistency.
246 }
247 }
248 }
249}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Register(IEventSink EventSink)
Registers an event sink with the event log. Call Unregister(IEventSink) to unregister it,...
Definition: Log.cs:29
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
static bool Unregister(IEventSink EventSink)
Unregisters an event sink from the event log.
Definition: Log.cs:46
Creates an even sink that stores incoming (logged) events in the local object database,...
override void Dispose()
IDisposable.Dispose()
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static bool HasProvider
If a database provider is registered.
Definition: Database.cs:79
static void Register(IDatabaseProvider DatabaseProvider)
Registers a database provider for use from the static Database class, throughout the lifetime of the ...
Definition: Database.cs:31
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:57
static Task< bool > Export(IDatabaseExport Output)
Performs an export of the database.
Definition: Database.cs:1134
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
Persists objects into binary files.
async Task Stop()
Called when processing ends.
async Task Flush()
Persists any pending changes.
async Task< string[]> RepairIfInproperShutdown(string XsltPath)
Checks if the database needs repairing. This is done by checking the last start and stop timetamps to...
Task Start()
Called when processing starts.
static Task< FilesProvider > CreateAsync(string Folder, string DefaultCollectionName, int BlockSize, int BlocksInCache, int BlobBlockSize, Encoding Encoding, int TimeoutMilliseconds, CustomKeyHandler CustomKeyMethod)
Persists objects into binary files.
void Dispose()
IDisposable.Dispose
Task Flush()
Persists any pending changes.