Skip to content

Commit 685d392

Browse files
committed
Adding some more thorough comments
1 parent 531c3b9 commit 685d392

File tree

2 files changed

+142
-67
lines changed

2 files changed

+142
-67
lines changed

src/LightningDB/LightningEnvironment.cs

Lines changed: 97 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public LightningEnvironment(string path, EnvironmentConfiguration configuration
2525
{
2626
if (string.IsNullOrWhiteSpace(path))
2727
throw new ArgumentException("Invalid directory name");
28-
28+
2929
var config = configuration ?? _config;
3030

3131
mdb_env_create(out _handle).ThrowOnError();
@@ -49,22 +49,22 @@ public LightningEnvironment(string path, EnvironmentConfiguration configuration
4949

5050
/// <summary>
5151
/// Gets or Sets the size of the memory map to use for this environment.
52-
/// The size of the memory map is also the maximum size of the database.
53-
/// The size should be a multiple of the OS page size.
54-
/// The default is 10485760 bytes.
52+
/// The size of the memory map is also the maximum size of the database.
53+
/// The size should be a multiple of the OS page size.
54+
/// The default is 10485760 bytes.
5555
/// </summary>
5656
/// <remarks>
57-
/// The value should be chosen as large as possible, to accommodate future growth of the database.
57+
/// The value should be chosen as large as possible, to accommodate future growth of the database.
5858
/// This function may only be called before the environment is opened.
59-
/// The size may be changed by closing and reopening the environment.
60-
/// Any attempt to set a size smaller than the space already consumed by the environment will be silently changed to the current size of the used space.
59+
/// The size may be changed by closing and reopening the environment.
60+
/// Any attempt to set a size smaller than the space already consumed by the environment will be silently changed to the current size of the used space.
6161
/// </remarks>
6262
public unsafe long MapSize
6363
{
6464
get => _config.MapSize;
6565
set
6666
{
67-
if (value == _config.MapSize)
67+
if (value == _config.MapSize)
6868
return;
6969

7070
if (_config.AutoReduceMapSizeIn32BitProcess && sizeof(nint) == 4)
@@ -77,8 +77,22 @@ public unsafe long MapSize
7777
}
7878

7979
/// <summary>
80-
/// Get the maximum number of threads for the environment.
80+
/// Gets or sets the maximum number of threads/reader slots for the environment.
8181
/// </summary>
82+
/// <remarks>
83+
/// This defines the number of slots in the lock table that is used to track readers in the
84+
/// environment. The default is 126. Starting a read-only transaction normally ties a lock table
85+
/// slot to the current thread until the environment closes or the thread exits. If
86+
/// <see cref="LightningTransaction.Reset"/> is called on the transaction, the slot can be reused immediately.
87+
///
88+
/// This function may only be called before the environment is opened.
89+
///
90+
/// If this function is never called, the default value will be used.
91+
///
92+
/// Note that the lock table size is also the maximum number of concurrent read-only transactions
93+
/// that can be in progress at once. Any attempt to start a read-only transaction when the lock
94+
/// table is full will result in an MDB_READERS_FULL error.
95+
/// </remarks>
8296
public int MaxReaders
8397
{
8498
get
@@ -99,8 +113,8 @@ public int MaxReaders
99113

100114
/// <summary>
101115
/// Set the maximum number of named databases for the environment.
102-
/// This function is only needed if multiple databases will be used in the environment.
103-
/// Simpler applications that use the environment as a single unnamed database can ignore this option.
116+
/// This function is only needed if multiple databases will be used in the environment.
117+
/// Simpler applications that use the environment as a single unnamed database can ignore this option.
104118
/// This function may only be called before the environment is opened.
105119
/// </summary>
106120
public int MaxDatabases
@@ -111,7 +125,7 @@ public int MaxDatabases
111125
if (IsOpened)
112126
throw new InvalidOperationException("Can't change MaxDatabases of opened environment");
113127

114-
if (value == _config.MaxDatabases)
128+
if (value == _config.MaxDatabases)
115129
return;
116130

117131
mdb_env_set_maxdbs(_handle, (uint)value).ThrowOnError();
@@ -121,7 +135,7 @@ public int MaxDatabases
121135
}
122136

123137
/// <summary>
124-
/// Get statistics about the LMDB environment.
138+
/// Get statistics about the LMDB environment.
125139
/// </summary>
126140
public Stats EnvironmentStats
127141
{
@@ -141,7 +155,7 @@ public Stats EnvironmentStats
141155
}
142156

143157
/// <summary>
144-
/// Gets information about the LMDB environment.
158+
/// Gets information about the LMDB environment.
145159
/// </summary>
146160
public EnvironmentInfo Info
147161
{
@@ -156,7 +170,7 @@ public EnvironmentInfo Info
156170
};
157171
}
158172
}
159-
173+
160174
/// <summary>
161175
/// Gets the maximum size of keys and MDB_DUPSORT data we can write.
162176
/// </summary>
@@ -211,12 +225,12 @@ public void Open(EnvironmentOpenFlags openFlags = EnvironmentOpenFlags.None, Uni
211225
/// Cursors may not span transactions; each cursor must be opened and closed within a single transaction.
212226
/// </summary>
213227
/// <param name="parent">
214-
/// If this parameter is non-NULL, the new transaction will be a nested transaction, with the transaction indicated by parent as its parent.
215-
/// Transactions may be nested to any level.
228+
/// If this parameter is non-NULL, the new transaction will be a nested transaction, with the transaction indicated by parent as its parent.
229+
/// Transactions may be nested to any level.
216230
/// A parent transaction may not issue any other operations besides BeginTransaction, Abort, or Commit while it has active child transactions.
217231
/// </param>
218232
/// <param name="beginFlags">
219-
/// Special options for this transaction.
233+
/// Special options for this transaction.
220234
/// </param>
221235
/// <returns>
222236
/// New LightningTransaction
@@ -237,7 +251,7 @@ public LightningTransaction BeginTransaction(LightningTransaction parent = null,
237251
/// Cursors may not span transactions; each cursor must be opened and closed within a single transaction.
238252
/// </summary>
239253
/// <param name="beginFlags">
240-
/// Special options for this transaction.
254+
/// Special options for this transaction.
241255
/// </param>
242256
/// <returns>
243257
/// New LightningTransaction
@@ -253,28 +267,56 @@ public LightningTransaction BeginTransaction(TransactionBeginFlags beginFlags)
253267
/// </summary>
254268
/// <param name="path">The directory in which the copy will reside. This directory must already exist and be writable but must otherwise be empty.</param>
255269
/// <param name="compact">Omit empty pages when copying.</param>
270+
/// <remarks>
271+
/// This call can trigger a significant file size growth if run in parallel with write transactions,
272+
/// because it employs a read-only transaction. If the environment has a large proportion of
273+
/// free pages, this can cause the backup to be larger than the actual data set.
274+
///
275+
/// It is possible to run this function with writeable transactions in progress. The copy will be
276+
/// a consistent snapshot of the environment at the time the copy is made, using a read-only transaction.
277+
///
278+
/// When the compact parameter is true (using the MDB_CP_COMPACT flag), the copy will omit free pages
279+
/// and use smaller page sizes when possible, resulting in a smaller data file.
280+
/// </remarks>
256281
public MDBResultCode CopyTo(string path, bool compact = false)
257282
{
258283
EnsureOpened();
259284

260-
var flags = compact
261-
? EnvironmentCopyFlags.Compact
285+
var flags = compact
286+
? EnvironmentCopyFlags.Compact
262287
: EnvironmentCopyFlags.None;
263-
288+
264289
return mdb_env_copy2(_handle, path, flags);
265290
}
266291

267292
/// <summary>
268-
/// Flush the data buffers to disk.
269-
/// Data is always written to disk when LightningTransaction.Commit is called, but the operating system may keep it buffered.
293+
/// Flush the data buffers to disk.
294+
/// Data is always written to disk when LightningTransaction.Commit is called, but the operating system may keep it buffered.
270295
/// MDB always flushes the OS buffers upon commit as well, unless the environment was opened with EnvironmentOpenFlags.NoSync or in part EnvironmentOpenFlags.NoMetaSync.
271296
/// </summary>
272297
/// <param name="force">If true, force a synchronous flush. Otherwise if the environment has the EnvironmentOpenFlags.NoSync flag set the flushes will be omitted, and with MDB_MAPASYNC they will be asynchronous.</param>
298+
/// <remarks>
299+
/// This call is not needed if your code does not care if/when data is physically written to the disk.
300+
/// It is the application's responsibility to ensure the persistence of critical data. The
301+
/// actual decision to use synchronous or asynchronous flushes typically depends on:
302+
///
303+
/// - Safety: Synchronous flushes guarantee that data has been written to disk before continuing.
304+
/// - Performance: Asynchronous flushes allow the OS to schedule disk writes optimally, which can be much faster.
305+
///
306+
/// If your environment was opened with <see cref="EnvironmentOpenFlags.NoSync"/>, calling Flush(false) will have no effect,
307+
/// while Flush(true) will still force a synchronous flush.
308+
///
309+
/// If your environment was opened with <see cref="EnvironmentOpenFlags.MapAsync"/>, calling Flush(false) will perform an
310+
/// asynchronous flush, while Flush(true) will still force a synchronous flush.
311+
///
312+
/// Even when this method is not called, the data will be persisted to disk when the OS flushes
313+
/// its buffers or when the environment is closed properly.
314+
/// </remarks>
273315
public MDBResultCode Flush(bool force)
274316
{
275317
return mdb_env_sync(_handle, force);
276318
}
277-
319+
278320
/// <summary>
279321
/// Gets or sets the environment flags.
280322
/// When setting flags, they will be either set or cleared based on the value.
@@ -301,35 +343,47 @@ public EnvironmentOpenFlags Flags
301343
set
302344
{
303345
EnsureOpened();
304-
346+
305347
// Get current flags
306348
mdb_env_get_flags(_handle, out var currentFlags).ThrowOnError();
307-
349+
308350
// Determine which flags to turn on and which to turn off by converting to uint
309351
var newFlags = (uint)value;
310352
var oldFlags = currentFlags;
311-
353+
312354
var flagsToEnable = newFlags & ~oldFlags; // Flags in new value but not in current
313355
var flagsToDisable = oldFlags & ~newFlags; // Flags in current but not in new value
314-
356+
315357
// Turn on new flags
316358
if (flagsToEnable != 0)
317359
{
318360
mdb_env_set_flags(_handle, flagsToEnable, true).ThrowOnError();
319361
}
320-
362+
321363
// Turn off removed flags
322364
if (flagsToDisable != 0)
323365
{
324366
mdb_env_set_flags(_handle, flagsToDisable, false).ThrowOnError();
325367
}
326368
}
327369
}
328-
370+
329371
/// <summary>
330372
/// Checks for stale readers in the environment and cleans them up.
331373
/// </summary>
332374
/// <returns>The number of stale readers that were cleared</returns>
375+
/// <remarks>
376+
/// Reader slots are tied to a thread for the lifetime of a transaction, but if a thread
377+
/// terminates abnormally while a transaction is active, the slot remains "owned" by the thread
378+
/// and is unavailable for reuse. This function identifies such stale readers and makes their
379+
/// slots available for reuse.
380+
///
381+
/// This function should be called periodically in long-running applications or when
382+
/// BeginTransaction returns <see cref="MDBResultCode.ReadersFull"/>.
383+
///
384+
/// Readers don't use any locks, so stale readers won't cause writers to wait. However, they do
385+
/// take up reader slots which could prevent other processes from starting read transactions.
386+
/// </remarks>
333387
public int CheckStaleReaders()
334388
{
335389
EnsureOpened();
@@ -338,26 +392,26 @@ public int CheckStaleReaders()
338392
throw new LightningException($"Failed to check stale readers", result);
339393
return deadReaders;
340394
}
341-
395+
342396
/// <summary>
343397
/// Gets a read access FileStream for the environment's file descriptor.
344398
/// </summary>
345399
/// <returns>A FileStream that wraps the environment's file descriptor</returns>
346400
public FileStream GetFileStream()
347401
{
348402
EnsureOpened();
349-
403+
350404
// Get the raw file descriptor
351405
mdb_env_get_fd(_handle, out var fd).ThrowOnError();
352-
406+
353407
// Create a SafeFileHandle from the file descriptor
354408
// Note: We set ownsHandle to false because LMDB owns the file descriptor
355409
var safeHandle = new Microsoft.Win32.SafeHandles.SafeFileHandle(fd, ownsHandle: false);
356-
410+
357411
// Create a FileStream from the SafeFileHandle
358412
return new FileStream(safeHandle, FileAccess.Read);
359413
}
360-
414+
361415
/// <summary>
362416
/// Copies an LMDB environment to the specified FileStream.
363417
/// This method accepts a standard .NET FileStream and extracts the file descriptor.
@@ -372,10 +426,10 @@ public MDBResultCode CopyToStream(FileStream fileStream, bool compact = false)
372426
{
373427
if (fileStream == null)
374428
throw new ArgumentNullException(nameof(fileStream));
375-
429+
376430
if (!fileStream.CanWrite)
377431
throw new ArgumentException("FileStream must be writable", nameof(fileStream));
378-
432+
379433
EnsureOpened();
380434

381435
// Get the SafeFileHandle from the FileStream
@@ -386,8 +440,8 @@ public MDBResultCode CopyToStream(FileStream fileStream, bool compact = false)
386440
// Get the file descriptor as IntPtr/nint
387441
var fd = safeHandle.DangerousGetHandle();
388442

389-
return compact
390-
? mdb_env_copyfd2(_handle, fd, EnvironmentCopyFlags.Compact)
443+
return compact
444+
? mdb_env_copyfd2(_handle, fd, EnvironmentCopyFlags.Compact)
391445
: mdb_env_copyfd(_handle, fd);
392446
}
393447

@@ -422,8 +476,8 @@ private void Dispose(bool disposing)
422476

423477
/// <summary>
424478
/// Dispose the environment and release the memory map.
425-
/// Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function.
426-
/// Attempts to use any such handles after calling this function will cause a SIGSEGV.
479+
/// Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function.
480+
/// Attempts to use any such handles after calling this function will cause a SIGSEGV.
427481
/// The environment handle will be freed and must not be used again after this call.
428482
/// </summary>
429483
public void Dispose()
@@ -435,4 +489,4 @@ public void Dispose()
435489
{
436490
Dispose(false);
437491
}
438-
}
492+
}

0 commit comments

Comments
 (0)