Exemple #1
0
        public async Task OnErrorAsync(FdbError code, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            switch (code)
            {
            case FdbError.TimedOut:
            case FdbError.PastVersion:
            {                     // wait a bit
                ++m_retryCount;
                if (m_retryCount > this.RetryLimit)
                {                         // max rety limit reached
                    throw new FdbException(code);
                }

                //HACKHACK: implement a real back-off delay logic
                await Task.Delay(15, cancellationToken).ConfigureAwait(false);

                this.Reset();

                return;
            }

            default:
            {
                throw new FdbException(code);
            }
            }
        }
Exemple #2
0
 /// <summary>Throws an exception if the code represents a failure</summary>
 internal static void DieOnError(FdbError code)
 {
     if (Failed(code))
     {
         throw MapToException(code);
     }
 }
Exemple #3
0
        /// <summary>Maps an error code into an Exception (to be throwned)</summary>
        /// <param name="code"></param>
        /// <returns>Exception object corresponding to the error code, or null if the code is not an error</returns>
        public static Exception MapToException(FdbError code)
        {
            if (code == FdbError.Success)
            {
                return(null);
            }

            string msg = GetErrorMessage(code);

            if (msg == null)
            {
                throw new FdbException(code, $"Unexpected error code {(int) code}");
            }

            //TODO: create a custom FdbException to be able to store the error code and error message
            switch (code)
            {
            case FdbError.TimedOut: return(new TimeoutException("Operation timed out"));

            case FdbError.LargeAllocFailed: return(new OutOfMemoryException("Large block allocation failed"));

            //TODO!
            default:
                return(new FdbException(code, msg));
            }
        }
 public override Task OnErrorAsync(FdbError code)
 {
     return(ExecuteAsync(
                new FdbTransactionLog.OnErrorCommand(code),
                (_tr, _cmd) => _tr.OnErrorAsync(_cmd.Code)
                ));
 }
 protected FoundationDbException(SerializationInfo info, StreamingContext context)
 {
     if (info != null)
     {
         ErrorCode = (FdbError)info.GetInt32(nameof(ErrorCode));
     }
 }
Exemple #6
0
        /// <summary>
        /// Ensures success by throwing an exception if <paramref name="error"/> does not indicate Success.
        /// </summary>
        /// <param name="error">The error code to check.</param>
        /// <exception cref="FoundationDbException">Thrown when <paramref name="error"/> does not indicate Success.</exception>
        public static void EnsureSuccess(this FdbError error)
        {
            if (error == FdbError.Success)
            {
                return;
            }

            ThrowFoundationDbException(error);
        }
Exemple #7
0
        public static FutureHandle TransactionOnError(TransactionHandle transaction, FdbError errorCode)
        {
            var future = NativeMethods.fdb_transaction_on_error(transaction, errorCode);

            Contract.Assert(future != null);
#if DEBUG_NATIVE_CALLS
            Debug.WriteLine("fdb_transaction_on_error(0x" + transaction.Handle.ToString("x") + ", " + errorCode + ") => 0x" + future.Handle.ToString("x"));
#endif
            return(future);
        }
Exemple #8
0
        private static string GetOrAddErrorMessage(FdbError error)
        {
            return(ErrorMessages.GetOrAdd(error, err =>
            {
                var msgPtr = fdb_get_error(err);

                string message = Marshal.PtrToStringAnsi(msgPtr);

                return message;
            }));
        }
Exemple #9
0
        /// <summary>
        /// Converts the given <paramref name="error"/> to an exception.
        /// </summary>
        /// <param name="error">The error code to convert to an <see cref="Exception"/>.</param>
        /// <returns>The exception encapsulating the error code.</returns>
        /// <exception cref="InvalidOperationException">Thrown when <paramref name="error"/> is Success.</exception>
        public static FoundationDbException ToException(this FdbError error)
        {
            if (error == FdbError.Success)
            {
                throw new InvalidOperationException("FdbError.Success is not an error.");
            }

            if (!ErrorMessages.TryGetValue(error, out string errorMessage))
            {
                errorMessage = GetOrAddErrorMessage(error);
            }

            return(new FoundationDbException(errorMessage, error));
        }
        public static async Task AssertThrowsFdbErrorAsync(Func <Task> asyncTest, FdbError expectedCode, string message = null, object[] args = null)
        {
            try
            {
                await asyncTest();

                Assert.Fail(message, args);
            }
            catch (AssertionException) { throw; }
            catch (Exception e)
            {
                Assert.That(e, Is.InstanceOf <FdbException>().With.Property("Code").EqualTo(expectedCode), message, args);
            }
        }
        /// <summary>Update the Task with the state of a ready Future</summary>
        /// <returns>True if we got a result, or false in case of error (or invalid state)</returns>
        private void HandleCompletion()
        {
            if (HasAnyFlags(FdbFuture.Flags.DISPOSED | FdbFuture.Flags.COMPLETED))
            {
                return;
            }

#if DEBUG_FUTURES
            Debug.WriteLine("FutureArray<" + typeof(T).Name + ">.Callback(...) handling completion on thread #" + Thread.CurrentThread.ManagedThreadId.ToString());
#endif

            try
            {
                UnregisterCancellationRegistration();

                List <Exception>?errors       = null;
                bool             cancellation = false;
                var selector = m_resultSelector;

                var results = selector != null ? new T[m_handles.Length] : null;

                for (int i = 0; i < m_handles.Length; i++)
                {
                    var handle = m_handles[i];

                    if (handle != null && !handle.IsClosed && !handle.IsInvalid)
                    {
                        FdbError err = FdbNative.FutureGetError(handle);
                        if (Fdb.Failed(err))
                        {                         // it failed...
                            if (err != FdbError.OperationCancelled)
                            {                     // get the exception from the error code
                                var ex = Fdb.MapToException(err) !;
                                (errors ??= new List <Exception>()).Add(ex);
                            }
                            else
                            {
                                cancellation = true;
                                break;
                            }
                        }
                        else
                        {                         // it succeeded...
                            // try to get the result...
                            if (selector != null)
                            {
                                //note: result selector will execute from network thread, but this should be our own code that only calls into some fdb_future_get_XXXX(), which should be safe...
                                results ![i] = selector(handle);
        /// <summary>
        /// Implements the recommended retry and backoff behavior for a transaction.
        ///
        /// This function knows which of the error codes generated by other query functions represent temporary error conditions and which represent application errors that should be handled by the application.
        /// It also implements an exponential backoff strategy to avoid swamping the database cluster with excessive retries when there is a high level of conflict between transactions.
        /// </summary>
        /// <param name="code">FdbError code thrown by the previous command</param>
        /// <returns>Returns a task that completes if the operation can be safely retried, or that rethrows the original exception if the operation is not retryable.</returns>
        public async Task OnErrorAsync(FdbError code)
        {
            EnsureCanRetry();

            await m_handler.OnErrorAsync(code, cancellationToken : m_cancellation).ConfigureAwait(false);

            // If fdb_transaction_on_error succeeds, that means that the transaction has been reset and is usable again
            var state = this.State;

            if (state != STATE_DISPOSED)
            {
                Interlocked.CompareExchange(ref m_state, STATE_READY, state);
            }

            RestoreDefaultSettings();
        }
        private static void SetResult(IntPtr handle, IntPtr futurePtr)
        {
            var fdbFutureGch = GCHandle.FromIntPtr(futurePtr);
            var fdbFuture    = (FdbFuture <T>)fdbFutureGch.Target;

            Debug.Assert(fdbFuture != null);

            using (fdbFuture)
            {
                TaskCompletionSource <T> task = fdbFuture._tcs;
                FdbError error = fdbFuture.GetError();

                if (error == FdbError.Success)
                {
                    T result = fdbFuture.GetResult();

                    task.SetResult(result);
                }
                else
                {
                    task.SetException(error.ToException());
                }
            }
        }
Exemple #14
0
 /// <summary>Returns true if the error code represents a failure</summary>
 public static bool Failed(FdbError code)
 {
     return(code != FdbError.Success);
 }
		public virtual Task OnErrorAsync(FdbError code)
		{
			return m_transaction.OnErrorAsync(code);
		}
 public virtual Task OnErrorAsync(FdbError code)
 {
     return(m_transaction.OnErrorAsync(code));
 }
		/// <summary>Maps an error code into an Exception (to be throwned)</summary>
		/// <param name="code"></param>
		/// <returns>Exception object corresponding to the error code, or null if the code is not an error</returns>
		public static Exception MapToException(FdbError code)
		{
			if (code == FdbError.Success) return null;

			string msg = GetErrorMessage(code);
			if (msg == null) throw new FdbException(code, String.Format("Unexpected error code {0}", (int)code));

			//TODO: create a custom FdbException to be able to store the error code and error message
			switch(code)
			{
				case FdbError.TimedOut: return new TimeoutException("Operation timed out");
				case FdbError.LargeAllocFailed: return new OutOfMemoryException("Large block allocation failed");
				//TODO!
				default:
					return new FdbException(code, msg);
			}
		}
		/// <summary>Throws an exception if the code represents a failure</summary>
		internal static void DieOnError(FdbError code)
		{
			if (Failed(code)) throw MapToException(code);
		}
Exemple #19
0
		/// <summary>
		/// Implements the recommended retry and backoff behavior for a transaction.
		/// 
		/// This function knows which of the error codes generated by other query functions represent temporary error conditions and which represent application errors that should be handled by the application. 
		/// It also implements an exponential backoff strategy to avoid swamping the database cluster with excessive retries when there is a high level of conflict between transactions.
		/// </summary>
		/// <param name="code">FdbError code thrown by the previous command</param>
		/// <returns>Returns a task that completes if the operation can be safely retried, or that rethrows the original exception if the operation is not retryable.</returns>
		public async Task OnErrorAsync(FdbError code)
		{
			EnsureCanRetry();

			await m_handler.OnErrorAsync(code, cancellationToken: m_cancellation).ConfigureAwait(false);

			// If fdb_transaction_on_error succeeds, that means that the transaction has been reset and is usable again
			var state = this.State;
			if (state != STATE_DISPOSED) Interlocked.CompareExchange(ref m_state, STATE_READY, state);

			RestoreDefaultSettings();
		}
		/// <summary>fdb_get_error</summary>
		public static string GetError(FdbError code)
		{
			return ToManagedString(NativeMethods.fdb_get_error(code));
		}
Exemple #21
0
		public FdbException(FdbError errorCode, string message, Exception innerException)
			: base(message, innerException)
		{
			this.Code = errorCode;
		}
Exemple #22
0
		public FdbException(FdbError errorCode, string message)
			: this(errorCode, message, null)
		{		
		}
Exemple #23
0
		public FdbException(FdbError errorCode)
			: this(errorCode, Fdb.GetErrorMessage(errorCode), null)
		{
		}
		public Task OnErrorAsync(FdbError code, CancellationToken cancellationToken)
		{
			var future = FdbNative.TransactionOnError(m_handle, code);
			return FdbFuture.CreateTaskFromHandle<object>(future, (h) => { ResetInternal(); return null; }, cancellationToken);
		}
		public static FutureHandle TransactionOnError(TransactionHandle transaction, FdbError errorCode)
		{
			var future = NativeMethods.fdb_transaction_on_error(transaction, errorCode);
			Contract.Assert(future != null);
#if DEBUG_NATIVE_CALLS
			Debug.WriteLine("fdb_transaction_on_error(0x" + transaction.Handle.ToString("x") + ", " + errorCode + ") => 0x" + future.Handle.ToString("x"));
#endif
			return future;
		}
Exemple #26
0
 /// <summary>Return the error message matching the specified error code</summary>
 public static string GetErrorMessage(FdbError code)
 {
     return(FdbNative.GetError(code));
 }
Exemple #27
0
        /// <summary>Start the Network Thread, using the currently selected API version level</summary>
        /// <remarks>If you need a specific API version level, it must be defined by either calling <see cref="UseApiVersion"/> before calling this method, or by using the <see cref="Start(int)"/> override. Otherwise, the default API version will be selected.</remarks>
        public static void Start()
        {
            if (s_started)
            {
                return;
            }

            //BUGBUG: Specs say we cannot restart the network thread anymore in the process after stoping it ! :(

            s_started = true;

            int apiVersion = s_apiVersion;

            if (apiVersion <= 0)
            {
                apiVersion = DefaultApiVersion;
            }

            if (Logging.On)
            {
                Logging.Info(typeof(Fdb), "Start", $"Selecting fdb API version {apiVersion}");
            }

            FdbError err = FdbNative.SelectApiVersion(apiVersion);

            if (err != FdbError.Success)
            {
                if (Logging.On)
                {
                    Logging.Error(typeof(Fdb), "Start", $"Failed to fdb API version {apiVersion}: {err}");
                }

                switch (err)
                {
                case FdbError.ApiVersionNotSupported:
                {                         // bad version was selected ?
                    // note: we already bound check the values before, so that means that fdb_c.dll is either an older version or an incompatible new version.
                    throw new FdbException(err, $"The API version {apiVersion} is not supported by the FoundationDB client library (fdb_c.dll) installed on this system. The binding only supports versions {GetMinApiVersion()} to {GetMaxApiVersion()}. You either need to upgrade the .NET binding or the FoundationDB client library to a newer version.");
                }

#if DEBUG
                case FdbError.ApiVersionAlreadySet:
                {                         // Temporary hack to allow multiple debugging using the cached host process in VS
                    Console.Error.WriteLine("FATAL: CANNOT REUSE EXISTING PROCESS! FoundationDB client cannot be restarted once stopped. Current process will be terminated.");
                    Environment.FailFast("FATAL: CANNOT REUSE EXISTING PROCESS! FoundationDB client cannot be restarted once stopped. Current process will be terminated.");
                    break;
                }
#endif
                }
                DieOnError(err);
            }
            s_apiVersion = apiVersion;

            if (!string.IsNullOrWhiteSpace(Fdb.Options.TracePath))
            {
                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", $"Will trace client activity in '{Fdb.Options.TracePath}'");
                }
                // create trace directory if missing...
                if (!SystemIO.Directory.Exists(Fdb.Options.TracePath))
                {
                    SystemIO.Directory.CreateDirectory(Fdb.Options.TracePath);
                }

                DieOnError(SetNetworkOption(FdbNetworkOption.TraceEnable, Fdb.Options.TracePath));
            }

            if (!string.IsNullOrWhiteSpace(Fdb.Options.TLSPlugin))
            {
                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", $"Will use custom TLS plugin '{Fdb.Options.TLSPlugin}'");
                }

                DieOnError(SetNetworkOption(FdbNetworkOption.TLSPlugin, Fdb.Options.TLSPlugin));
            }

            if (Fdb.Options.TLSCertificateBytes.IsPresent)
            {
                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", $"Will load TLS root certificate and private key from memory ({Fdb.Options.TLSCertificateBytes.Count} bytes)");
                }

                DieOnError(SetNetworkOption(FdbNetworkOption.TLSCertBytes, Fdb.Options.TLSCertificateBytes));
            }
            else if (!string.IsNullOrWhiteSpace(Fdb.Options.TLSCertificatePath))
            {
                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", $"Will load TLS root certificate and private key from '{Fdb.Options.TLSCertificatePath}'");
                }

                DieOnError(SetNetworkOption(FdbNetworkOption.TLSCertPath, Fdb.Options.TLSCertificatePath));
            }

            if (Fdb.Options.TLSPrivateKeyBytes.IsPresent)
            {
                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", $"Will load TLS private key from memory ({Fdb.Options.TLSPrivateKeyBytes.Count} bytes)");
                }

                DieOnError(SetNetworkOption(FdbNetworkOption.TLSKeyBytes, Fdb.Options.TLSPrivateKeyBytes));
            }
            else if (!string.IsNullOrWhiteSpace(Fdb.Options.TLSPrivateKeyPath))
            {
                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", $"Will load TLS private key from '{Fdb.Options.TLSPrivateKeyPath}'");
                }

                DieOnError(SetNetworkOption(FdbNetworkOption.TLSKeyPath, Fdb.Options.TLSPrivateKeyPath));
            }

            if (Fdb.Options.TLSVerificationPattern.IsPresent)
            {
                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", $"Will verify TLS peers with pattern '{Fdb.Options.TLSVerificationPattern}'");
                }

                DieOnError(SetNetworkOption(FdbNetworkOption.TLSVerifyPeers, Fdb.Options.TLSVerificationPattern));
            }

            try { }
            finally
            {
                // register with the AppDomain to ensure that everyting is cleared when the process exists
                s_appDomainUnloadHandler = (sender, args) =>
                {
                    if (s_started)
                    {
                        //note: since app domain is unloading, the logger may also have already stopped...
                        if (Logging.On)
                        {
                            Logging.Verbose(typeof(Fdb), "AppDomainUnloadHandler", "AppDomain is unloading, stopping FoundationDB Network Thread...");
                        }
                        Stop();
                    }
                };
                AppDomain.CurrentDomain.DomainUnload += s_appDomainUnloadHandler;
                AppDomain.CurrentDomain.ProcessExit  += s_appDomainUnloadHandler;

                if (Logging.On)
                {
                    Logging.Verbose(typeof(Fdb), "Start", "Setting up Network Thread...");
                }

                DieOnError(FdbNative.SetupNetwork());
                s_started = true;                 //BUGBUG: already set at the start of the method. Maybe we need state flags ?
            }

            if (Logging.On)
            {
                Logging.Info(typeof(Fdb), "Start", "Network thread has been set up");
            }

            StartEventLoop();
        }
Exemple #28
0
 public FdbException(FdbError errorCode)
     : this(errorCode, Fdb.GetErrorMessage(errorCode), null)
 {
 }
		/// <summary>Returns true if the error code represents a failure</summary>
		public static bool Failed(FdbError code)
		{
			return code != FdbError.Success;
		}
Exemple #30
0
 public FdbException(FdbError errorCode, string message)
     : this(errorCode, message, null)
 {
 }
		/// <summary>Return the error message matching the specified error code</summary>
		public static string GetErrorMessage(FdbError code)
		{
			return FdbNative.GetError(code);
		}
Exemple #32
0
 public FdbException(FdbError errorCode, string message, Exception innerException)
     : base(message, innerException)
 {
     this.Code = errorCode;
 }
 public virtual Task OnErrorAsync(FdbError code)
 {
     ThrowIfDisposed();
     return(m_transaction.OnErrorAsync(code));
 }
        /// <summary>Update the Task with the state of a ready Future</summary>
        /// <param name="fromCallback">If true, we are called from the network thread</param>
        /// <returns>True if we got a result, or false in case of error (or invalid state)</returns>
        private void HandleCompletion(bool fromCallback)
        {
            // note: if fromCallback is true, we are running on the network thread
            // this means that we have to signal the TCS from the threadpool, if not continuations on the task may run inline.
            // this is very frequent when we are called with await, or ContinueWith(..., TaskContinuationOptions.ExecuteSynchronously)

            if (HasAnyFlags(FdbFuture.Flags.DISPOSED | FdbFuture.Flags.COMPLETED))
            {
                return;
            }

#if DEBUG_FUTURES
            var sw = Stopwatch.StartNew();
#endif
            try
            {
                var handle = m_handle;
                if (handle != null && !handle.IsClosed && !handle.IsInvalid)
                {
                    UnregisterCancellationRegistration();

                    FdbError err = FdbNative.FutureGetError(handle);
                    if (Fdb.Failed(err))
                    {                     // it failed...
#if DEBUG_FUTURES
                        Debug.WriteLine("Future<" + typeof(T).Name + "> has FAILED: " + err);
#endif
                        if (err != FdbError.OperationCancelled)
                        {                         // get the exception from the error code
                            var ex = Fdb.MapToException(err);
                            SetFaulted(ex, fromCallback);
                            return;
                        }
                        //else: will be handle below
                    }
                    else
                    {                     // it succeeded...
                        // try to get the result...
#if DEBUG_FUTURES
                        Debug.WriteLine("Future<" + typeof(T).Name + "> has completed successfully");
#endif
                        var selector = m_resultSelector;
                        if (selector != null)
                        {
                            //note: result selector will execute from network thread, but this should be our own code that only calls into some fdb_future_get_XXXX(), which should be safe...
                            var result = selector(handle);
                            SetResult(result, fromCallback);
                            return;
                        }
                        //else: it will be handled below
                    }
                }

                // most probably the future was cancelled or we are shutting down...
                SetCanceled(fromCallback);
            }
            catch (Exception e)
            {             // something went wrong
                if (e is ThreadAbortException)
                {
                    SetCanceled(fromCallback);
                    throw;
                }
                SetFaulted(e, fromCallback);
            }
            finally
            {
#if DEBUG_FUTURES
                sw.Stop();
                Debug.WriteLine("Future<" + typeof(T).Name + "> callback completed in " + sw.Elapsed.TotalMilliseconds.ToString() + " ms");
#endif
                TryCleanup();
            }
        }
Exemple #35
0
 public OnErrorCommand(FdbError code)
 {
     this.Code = code;
 }
Exemple #36
0
        /// <inheritdoc />
        public void AddConflictRange(ReadOnlySpan <byte> beginKeyInclusive, ReadOnlySpan <byte> endKeyExclusive, FdbConflictRangeType type)
        {
            FdbError err = FdbNative.TransactionAddConflictRange(m_handle, beginKeyInclusive, endKeyExclusive, type);

            Fdb.DieOnError(err);
        }
		public static async Task AssertThrowsFdbErrorAsync([NotNull] Func<Task> asyncTest, FdbError expectedCode, string message = null, object[] args = null)
		{
			try
			{
				await asyncTest();
				Assert.Fail(message, args);
			}
			catch (AssertionException) { throw; }
			catch (Exception e)
			{
				Assert.That(e, Is.InstanceOf<FdbException>().With.Property("Code").EqualTo(expectedCode), message, args);
			}
		}
			public static extern FutureHandle fdb_transaction_on_error(TransactionHandle transaction, FdbError error);
Exemple #39
0
 /// <summary>Returns true if the error code represents a success</summary>
 public static bool Success(FdbError code)
 {
     return(code == FdbError.Success);
 }
			Task IFdbReadOnlyTransaction.OnErrorAsync(FdbError code)
			{
				throw new NotSupportedException("You cannot retry on a Snapshot view of a transaction.");
			}
		public virtual Task OnErrorAsync(FdbError code)
		{
			ThrowIfDisposed();
			return m_transaction.OnErrorAsync(code);
		}
Exemple #42
0
 public static extern IntPtr fdb_get_error(FdbError code);
Exemple #43
0
 Task IFdbReadOnlyTransaction.OnErrorAsync(FdbError code)
 {
     throw new NotSupportedException("You cannot retry on a Snapshot view of a transaction.");
 }
		/// <summary>Returns true if the error code represents a success</summary>
		public static bool Success(FdbError code)
		{
			return code == FdbError.Success;
		}
Exemple #45
0
 public static extern FutureHandle fdb_transaction_on_error(TransactionHandle transaction, FdbError error);
			public static extern IntPtr fdb_get_error(FdbError code);
Exemple #47
0
        public Task OnErrorAsync(FdbError code, CancellationToken ct)
        {
            var future = FdbNative.TransactionOnError(m_handle, code);

            return(FdbFuture.CreateTaskFromHandle <object?>(future, (h) => { ResetInternal(); return null; }, ct));
        }
Exemple #48
0
 /// <summary>fdb_get_error</summary>
 public static string GetError(FdbError code)
 {
     return(ToManagedString(NativeMethods.fdb_get_error(code)));
 }