internal BlockMemoryPool(string path, uint maxLogSizeMb, LMDBEnvironmentFlags envFlags, Wpid ownerId, int maxBufferLength = RmDefaultMaxBufferLength, int maxBuffersPerBucket = RmDefaultMaxBuffersPerBucket) : base(path, maxLogSizeMb, envFlags, ownerId, maxBufferLength, maxBuffersPerBucket, rentAlwaysClean: true) { }
// TODO this is all internal with very limited usage, but refactor public BufferRefAllocator(string directoryPath, LMDBEnvironmentFlags envFlags, Wpid wpid, int pageSize = 4096, long maxTotalSize = 64L * 1024 * 1024 * 1024, uint envSizeMb = 128) { _wpid = wpid; _pageSize = pageSize; _maxTotalSize = maxTotalSize; // TODO Log size to settings. Also handle MapFull later and reduce size. SetupEnv(directoryPath, envSizeMb, envFlags); InitDbs(); }
internal unsafe SharedMemoryPool(string path, uint maxLogSizeMb, LMDBEnvironmentFlags envFlags, Wpid ownerId, int rmMaxBufferLength = RmDefaultMaxBufferLength, int rmMaxBuffersPerBucket = RmDefaultMaxBuffersPerBucket, bool rentAlwaysClean = false) : base( (pool, bucketSize) => { if (bucketSize > pool.MaxBufferSize) { BuffersThrowHelper.ThrowBadLength(); } var smbp = (SharedMemoryPool)pool; #pragma warning disable 618 var sm = smbp.RentNative(bucketSize); Debug.Assert(!sm.IsDisposed); Debug.Assert(sm.ReferenceCount == 0); Debug.Assert(Unsafe.ReadUnaligned <uint>(sm.HeaderPointer) == HeaderFlags.IsOwned); #pragma warning restore 618 // TODO review if Releasing -> IsOwned should keep disposed state? // RMP calls CreateNew outside lock, so if we call RentNative only here // then it could return IsOwned without IsDisposed. But we a technically // inside the pool until this factory returns. Buffers from RentNative // should be unusable without explicit un-dispose action. // Set counter to zero sm.CounterRef &= ~AtomicCounter.CountMask; return(sm); }, RmMinPoolBufferLength, Math.Max(RmDefaultMaxBufferLength, Math.Min(RmMaxPoolBufferLength, BitUtil.FindNextPositivePowerOfTwo(rmMaxBufferLength))), // from ProcCount to DefaultMaxNumberOfBuffersPerBucket x 2 Math.Max(Environment.ProcessorCount, Math.Min(RmDefaultMaxBuffersPerBucket * 2, rmMaxBuffersPerBucket)), MaxBucketsToTry, rentAlwaysClean: rentAlwaysClean) { if (ownerId <= 0) { ThrowHelper.ThrowArgumentOutOfRangeException("ownerId <= 0"); } Directory.CreateDirectory(path); _bra = new BufferRefAllocator(path, envFlags, ownerId, PageSize, maxLogSizeMb * 1024 * 1024L); _buckets = new SharedMemoryBuckets(Path.Combine(path, "buckets"), pageSize: PageSize, maxBucketIndex: BufferRef.MaxBucketIdx); StartMonitoringTask(); }
private void SetupEnv(string path, uint mapSizeMb, LMDBEnvironmentFlags flags) { var env = LMDBEnvironment.Create(path, flags, disableAsync: true, disableReadTxnAutoreset: true); env.MapSize = mapSizeMb * 1024L * 1024L; env.MaxReaders = 1024; env.Open(); var deadReaders = env.ReaderCheck(); if (deadReaders > 0) { Trace.TraceWarning($"Cleared {deadReaders} in SharedMemoryBufferPool"); } _env = env; }
public StreamBlockManager(string path, LMDBEnvironmentFlags envFlags, Wpid wpid, IStreamBlockStorage blockStorage = null, long maxTotalSize = 1024L * 1024 * 1024 * 1024 * 1024, uint envSizeMb = 1024) // 1Gb takes 2 MB of TLB, do not make it too large TODO calculate realistic required size that is derived from maxTotalSize when using smallest buffers : base(path, envFlags, wpid, PageSize, maxTotalSize, envSizeMb) { _blocksDb = Environment.OpenDatabase("_streamBlocks", new DatabaseConfig(DbFlags.Create | DbFlags.DuplicatesSort | DbFlags.IntegerDuplicates | DbFlags.DuplicatesFixed) { DupSortPrefix = 64 * 64 } ); _pidDb = Environment.OpenDatabase("_streamLogState", new DatabaseConfig(DbFlags.Create | DbFlags.IntegerKey)); _buckets = new SharedMemoryBuckets(Path.Combine(path, "buckets"), pageSize: PageSize, maxBucketIndex: BufferRef.MaxBucketIdx); _blockStorage = blockStorage; }
/// <summary> /// Creates a new instance of Environment. /// </summary> /// <param name="directory">Relative directory for storing database files.</param> /// <param name="openFlags">Database open options.</param> /// <param name="accessMode">Unix file access privelegies (optional). Only makes sense on unix operationg systems.</param> /// <param name="disableAsync">Disable dedicated writer thread and NO_TLS option. /// Fire-and-forget option for write transaction will throw. /// .NET async cannot be used in transaction body. True by default. </param> /// <param name="disableReadTxnAutoreset">Abort read-only transactions instead of resetting them. Should be true for multiple (a lot of) processes accessing the same env.</param> public static LMDBEnvironment Create(string directory, LMDBEnvironmentFlags openFlags = LMDBEnvironmentFlags.None, UnixAccessMode accessMode = UnixAccessMode.Default, bool disableAsync = true, bool disableReadTxnAutoreset = false) { #pragma warning disable 618 openFlags = openFlags | LMDBEnvironmentFlags.NoTls; #pragma warning restore 618 // this is machine-local storage for each user. if (string.IsNullOrWhiteSpace(directory)) { throw new ArgumentNullException(nameof(directory)); } var env = _openEnvs.GetOrAdd(directory, (dir) => new LMDBEnvironment(dir, openFlags, accessMode, disableAsync, disableReadTxnAutoreset)); if (env._openFlags != openFlags || env._accessMode != accessMode) { throw new InvalidOperationException("Environment is already open in this process with different flags and access mode."); } env._instanceCount++; return(env); }
private LMDBEnvironment(string directory, LMDBEnvironmentFlags openFlags = LMDBEnvironmentFlags.None, UnixAccessMode accessMode = UnixAccessMode.Default, bool disableWriterThread = false, bool disableReadTxnAutoreset = false) { NativeMethods.AssertExecute(NativeMethods.mdb_env_create(out var envHandle)); _handle = envHandle; _accessMode = accessMode; _disableReadTxnAutoreset = disableReadTxnAutoreset; _directory = directory; _openFlags = openFlags; try { CreateDirectoryIfMissing(); } catch { NativeMethods.mdb_env_close(envHandle); throw; } MaxDatabases = Config.DbEnvironment.DefaultMaxDatabases; // Writer Task // In the current process writes are serialized via the blocking queue // Accross processes, writes are synchronized via WriteTxnGate (TODO!) _cts = new CancellationTokenSource(); if (!disableWriterThread) { _writeQueue = new BlockingCollection <Delegates>(); var threadStart = new ThreadStart(() => { while (!_writeQueue.IsCompleted) { try { // BLOCKING var delegates = _writeQueue.Take(_cts.Token); var transactionImpl = TransactionImpl.Create(this, TransactionBeginFlags.ReadWrite); try { // TODO for some methods such as mbd_put we should have a C method // that begins/end txn automatically // we still need to pass call to it here, but we could avoid txn creation // and two P/Invokes var txn = new Transaction(transactionImpl); if (delegates.WriteFunction != null) { var res = delegates.WriteFunction(txn); delegates.Tcs?.SetResult(res); } else if (delegates.WriteAction != null) { delegates.WriteAction(txn); delegates.Tcs?.SetResult(null); } else { Environment.FailFast("Wrong writer thread setup"); } } catch (Exception e) { delegates.Tcs?.SetException(e); transactionImpl?.Abort(); } finally { transactionImpl?.Dispose(); } } catch (InvalidOperationException) { } } _writeTaskCompletion.SetResult(null); }); var writeThread = new Thread(threadStart) { Name = "LMDB Writer thread", IsBackground = true }; writeThread.Start(); } }
public static extern int mdb_env_open(EnvironmentHandle env, IntPtr path, LMDBEnvironmentFlags flags, int mode);
public static extern int mdb_env_open(IntPtr env, IntPtr path, LMDBEnvironmentFlags flags, UnixAccessMode mode);