public static async Task <IFdbDatabase> OpenNamedPartitionAsync(string clusterFile, string dbName, [NotNull] IEnumerable <string> path, bool readOnly, CancellationToken ct) { if (path == null) { throw new ArgumentNullException(nameof(path)); } var partitionPath = path.ToList(); if (partitionPath.Count == 0) { throw new ArgumentException("The path to the named partition cannot be empty", nameof(path)); } // looks at the global partition table for the specified named partition // By convention, all named databases will be under the "/Databases" folder FdbDatabase db = null; var rootSpace = KeySubspace.Empty; try { db = await Fdb.OpenInternalAsync(clusterFile, dbName, rootSpace, readOnly : false, ct : ct).ConfigureAwait(false); var rootLayer = FdbDirectoryLayer.Create(rootSpace); if (Logging.On) { Logging.Verbose(typeof(Fdb.Directory), "OpenNamedPartitionAsync", $"Opened root layer of database {db.Name} using cluster file '{db.Cluster.Path}'"); } // look up in the root layer for the named partition var descriptor = await rootLayer.CreateOrOpenAsync(db, partitionPath, layer : FdbDirectoryPartition.LayerId, ct : ct).ConfigureAwait(false); if (Logging.On) { Logging.Verbose(typeof(Fdb.Directory), "OpenNamedPartitionAsync", $"Found named partition '{descriptor.FullName}' at prefix {descriptor}"); } // we have to chroot the database to the new prefix, and create a new DirectoryLayer with a new '/' rootSpace = descriptor.Copy(); //note: create a copy of the key //TODO: find a nicer way to do that! db.ChangeRoot(rootSpace, FdbDirectoryLayer.Create(rootSpace, partitionPath), readOnly); if (Logging.On) { Logging.Info(typeof(Fdb.Directory), "OpenNamedPartitionAsync", $"Opened partition {descriptor.FullName} at {db.GlobalSpace}, using directory layer at {db.Directory.DirectoryLayer.NodeSubspace}"); } return(db); } catch (Exception e) { db?.Dispose(); if (Logging.On) { Logging.Exception(typeof(Fdb.Directory), "OpenNamedPartitionAsync", e); } throw; } }
/// <summary>Set an option on this cluster that does not take any parameter</summary> /// <param name="option">Option to set</param> public void SetOption(FdbClusterOption option) { ThrowIfDisposed(); Fdb.EnsureNotOnNetworkThread(); if (Logging.On && Logging.IsVerbose) { Logging.Verbose(this, "SetOption", $"Setting cluster option {option.ToString()}"); } m_handler.SetOption(option, Slice.Nil); }
/// <summary>Set an option on this cluster that takes an integer value</summary> /// <param name="option">Option to set</param> /// <param name="value">Value of the parameter</param> public void SetOption(FdbClusterOption option, long value) { ThrowIfDisposed(); Fdb.EnsureNotOnNetworkThread(); if (Logging.On && Logging.IsVerbose) { Logging.Verbose(this, "SetOption", $"Setting cluster option {option.ToString()} to {value}"); } var data = Slice.FromFixed64(value); m_handler.SetOption(option, data); }
/// <summary>Set an option on this cluster that takes a string value</summary> /// <param name="option">Option to set</param> /// <param name="value">Value of the parameter (can be null)</param> public void SetOption(FdbClusterOption option, string value) { ThrowIfDisposed(); Fdb.EnsureNotOnNetworkThread(); if (Logging.On && Logging.IsVerbose) { Logging.Verbose(this, "SetOption", $"Setting cluster option {option.ToString()} to '{value ?? "<null>"}'"); } var data = FdbNative.ToNativeString(value, nullTerminated: true); m_handler.SetOption(option, data); }
public static async Task <IFdbDatabase> OpenNamedPartitionAsync(string clusterFile, string dbName, [NotNull] IEnumerable <string> path, bool readOnly, CancellationToken ct) { Contract.NotNull(path, nameof(path)); var partitionPath = (path as string[]) ?? path.ToArray(); if (partitionPath.Length == 0) { throw new ArgumentException("The path to the named partition cannot be empty", nameof(path)); } var options = new FdbConnectionOptions { ClusterFile = clusterFile, DbName = dbName, PartitionPath = partitionPath, }; var db = await Fdb.OpenInternalAsync(options, ct).ConfigureAwait(false); return(db); }
/// <summary>Throws if the transaction is not in a valid state (for reading/writing) and that we can proceed with a read or write operation</summary> /// <param name="allowFromNetworkThread">If true, this operation is allowed to run from a callback on the network thread and should NEVER block.</param> /// <param name="allowFailedState">If true, this operation can run even if the transaction is in a failed state.</param> /// <exception cref="System.ObjectDisposedException">If Dispose as already been called on the transaction</exception> /// <exception cref="System.InvalidOperationException">If CommitAsync() or Rollback() have already been called on the transaction, or if the database has been closed</exception> internal void EnsureStilValid(bool allowFromNetworkThread = false, bool allowFailedState = false) { // We must not be disposed if (allowFailedState ? this.State == STATE_DISPOSED : this.State != STATE_READY) { ThrowOnInvalidState(this); } // The cancellation token should not be signaled m_cancellation.ThrowIfCancellationRequested(); // We cannot be called from the network thread (or else we will deadlock) if (!allowFromNetworkThread) { Fdb.EnsureNotOnNetworkThread(); } // Ensure that the DB is still opened and that this transaction is still registered with it this.Database.EnsureTransactionIsValid(this); // we are ready to go ! }
/// <summary>Checks that this type of mutation is supported by the currently selected API level</summary> /// <param name="mutation">Mutation type</param> /// <param name="selectedApiVersion">Select API level (200, 300, ...)</param> /// <exception cref="FdbException">An error with code <see cref="FdbError.InvalidMutationType"/> if the type of mutation is not supported by this API level.</exception> private static void EnsureMutationTypeIsSupported(FdbMutationType mutation, int selectedApiVersion) { if (selectedApiVersion < 200) { // mutations were not available at this time if (Fdb.GetMaxApiVersion() >= 200) { // but the installed client could support it throw new FdbException(FdbError.InvalidMutationType, "Atomic mutations are only supported starting from API level 200. You need to select API level 200 or more at the start of your process."); } else { // not supported by the local client throw new FdbException(FdbError.InvalidMutationType, "Atomic mutations are only supported starting from client version 2.x. You need to update the version of the client, and select API level 200 or more at the start of your process."); } } if (mutation == FdbMutationType.Add || mutation == FdbMutationType.BitAnd || mutation == FdbMutationType.BitOr || mutation == FdbMutationType.BitXor) { // these mutations are available since v200 return; } if (mutation == FdbMutationType.Max || mutation == FdbMutationType.Min) { // these mutations are available since v300 if (selectedApiVersion < 300) { if (Fdb.GetMaxApiVersion() >= 300) { throw new FdbException(FdbError.InvalidMutationType, "Atomic mutations Max and Min are only supported starting from API level 300. You need to select API level 300 or more at the start of your process."); } else { throw new FdbException(FdbError.InvalidMutationType, "Atomic mutations Max and Min are only supported starting from client version 3.x. You need to update the version of the client, and select API level 300 or more at the start of your process.."); } } // ok! return; } // this could be a new mutation type, or an invalid value. throw new FdbException(FdbError.InvalidMutationType, "An invalid mutation type was issued. If you are attempting to call a new mutation type, you will need to update the version of this assembly, and select the latest API level."); }
/// <summary>Stops the thread running the FDB event loop</summary> private static void StopEventLoop() { if (s_eventLoopStarted) { // We cannot be called from the network thread itself, or else we will dead lock ! Fdb.EnsureNotOnNetworkThread(); if (Logging.On) { Logging.Verbose(typeof(Fdb), "StopEventLoop", "Stopping network thread..."); } s_eventLoopStopRequested = true; var err = FdbNative.StopNetwork(); if (err != FdbError.Success) { if (Logging.On) { Logging.Warning(typeof(Fdb), "StopEventLoop", $"Failed to stop event loop: {err.ToString()}"); } } s_eventLoopStarted = false; var thread = s_eventLoop; if (thread != null && thread.IsAlive) { // BUGBUG: specs says that we need to wait for the network thread to stop gracefuly, or else data integrity may not be guaranteed... // We should wait for a bit, and only attempt to Abort() the thread after a timeout (30sec ? more ?) // keep track of how much time it took to stop... var duration = Stopwatch.StartNew(); try { //TODO: replace with a ManualResetEvent that would get signaled at the end of the event loop ? while (thread.IsAlive && duration.Elapsed.TotalSeconds < 5) { // wait a bit... Thread.Sleep(250); } if (thread.IsAlive) { if (Logging.On) { Logging.Warning(typeof(Fdb), "StopEventLoop", $"The fdb network thread has not stopped after {duration.Elapsed.TotalSeconds:N0} seconds. Forcing shutdown..."); } // Force a shutdown thread.Abort(); bool stopped = thread.Join(TimeSpan.FromSeconds(30)); //REVIEW: is this even usefull? If the thread is stuck in a native P/Invoke call, it won't get notified until it returns to managed code ... // => in that case, we have a zombie thread on our hands... if (!stopped) { if (Logging.On) { Logging.Warning(typeof(Fdb), "StopEventLoop", $"The fdb network thread failed to stop after more than {duration.Elapsed.TotalSeconds:N0} seconds. Transaction integrity may not be guaranteed."); } } } } catch (ThreadAbortException) { // Should not happen, unless we are called from a thread that is itself being stopped ? } finally { s_eventLoop = null; duration.Stop(); if (duration.Elapsed.TotalSeconds >= 20) { if (Logging.On) { Logging.Warning(typeof(Fdb), "StopEventLoop", $"The fdb network thread took a long time to stop ({duration.Elapsed.TotalSeconds:N0} seconds)."); } } } } } }
public FdbException(FdbError errorCode) : this(errorCode, Fdb.GetErrorMessage(errorCode), null) { }
public static IFdbDatabaseScopeProvider AsDatabaseProvider([NotNull] this IFdbDatabase db) { return(Fdb.CreateRootScope(db)); }