All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mongodb.operation.CommandOperationHelper Maven / Gradle / Ivy

Go to download

The MongoDB Java Driver uber-artifact, containing mongodb-driver, mongodb-driver-core, and bson

There is a newer version: 3.12.14
Show newest version
/*
 * Copyright 2008-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mongodb.operation;

import com.mongodb.MongoCommandException;
import com.mongodb.MongoException;
import com.mongodb.MongoNodeIsRecoveringException;
import com.mongodb.MongoNotPrimaryException;
import com.mongodb.MongoSocketException;
import com.mongodb.MongoWriteConcernException;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.binding.AsyncConnectionSource;
import com.mongodb.binding.AsyncReadBinding;
import com.mongodb.binding.AsyncWriteBinding;
import com.mongodb.binding.ConnectionSource;
import com.mongodb.binding.ReadBinding;
import com.mongodb.binding.WriteBinding;
import com.mongodb.connection.AsyncConnection;
import com.mongodb.connection.Connection;
import com.mongodb.connection.ConnectionDescription;
import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.operation.WriteConcernHelper;
import com.mongodb.internal.validator.NoOpFieldNameValidator;
import com.mongodb.lang.Nullable;
import com.mongodb.session.SessionContext;
import org.bson.BsonDocument;
import org.bson.FieldNameValidator;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.Decoder;

import java.util.List;

import static com.mongodb.ReadPreference.primary;
import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
import static com.mongodb.operation.OperationHelper.AsyncCallableWithConnectionAndSource;
import static com.mongodb.operation.OperationHelper.CallableWithConnectionAndSource;
import static com.mongodb.operation.OperationHelper.LOGGER;
import static com.mongodb.operation.OperationHelper.canRetryWrite;
import static com.mongodb.operation.OperationHelper.releasingCallback;
import static com.mongodb.operation.OperationHelper.withConnection;
import static com.mongodb.operation.OperationHelper.withReleasableConnection;
import static java.lang.String.format;
import static java.util.Arrays.asList;

final class CommandOperationHelper {

    interface CommandTransformer {

        /**
         * Yield an appropriate result object for the input object.
         *
         * @param t the input object
         * @return the function result
         */
        R apply(T t, ServerAddress serverAddress);
    }

    static class IdentityTransformer implements CommandTransformer {
        @Override
        public T apply(final T t, final ServerAddress serverAddress) {
            return t;
        }
    }

    static CommandTransformer writeConcernErrorTransformer() {
        return new CommandTransformer() {
            @Override
            public Void apply(final BsonDocument result, final ServerAddress serverAddress) {
                WriteConcernHelper.throwOnWriteConcernError(result, serverAddress);
                return null;
            }
        };
    }

    interface CommandCreator {
        BsonDocument create(ServerDescription serverDescription, ConnectionDescription connectionDescription);
    }

    /* Read Binding Helpers */

    static BsonDocument executeWrappedCommandProtocol(final ReadBinding binding, final String database, final BsonDocument command) {
        return executeWrappedCommandProtocol(binding, database, command, new BsonDocumentCodec());
    }

    static  T executeWrappedCommandProtocol(final ReadBinding binding, final String database, final BsonDocument command,
                                               final CommandTransformer transformer) {
        return executeWrappedCommandProtocol(binding, database, command, new BsonDocumentCodec(), transformer);
    }

    static  T executeWrappedCommandProtocol(final ReadBinding binding, final String database, final BsonDocument command,
                                               final Decoder decoder) {
        return executeWrappedCommandProtocol(binding, database, command, decoder, new IdentityTransformer());
    }

    static  T executeWrappedCommandProtocol(final ReadBinding binding, final String database, final BsonDocument command,
                                                  final Decoder decoder, final CommandTransformer transformer) {
        ConnectionSource source = binding.getReadConnectionSource();
        try {
            return transformer.apply(executeWrappedCommandProtocol(database, command, decoder, source,
                                                                   binding.getReadPreference()),
                                     source.getServerDescription().getAddress());
        } finally {
            source.release();
        }
    }

    static BsonDocument executeWrappedCommandProtocol(final ReadBinding binding, final String database, final BsonDocument command,
                                               final Connection connection) {
        return executeWrappedCommandProtocol(binding, database, command, connection, new IdentityTransformer());
    }

    static  T executeWrappedCommandProtocol(final ReadBinding binding, final String database, final BsonDocument command,
                                               final Connection connection, final CommandTransformer transformer) {
        return executeWrappedCommandProtocol(binding, database, command, new BsonDocumentCodec(), connection, transformer);
    }

    static  T executeWrappedCommandProtocol(final ReadBinding binding, final String database, final BsonDocument command,
                                               final Decoder decoder, final Connection connection,
                                               final CommandTransformer transformer) {
        return executeWrappedCommandProtocol(database, command, decoder, connection, binding.getReadPreference(), transformer,
                binding.getSessionContext());
    }

    /* Write Binding Helpers */

    static BsonDocument executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command) {
        return executeWrappedCommandProtocol(binding, database, command, new IdentityTransformer());
    }

    static  T executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                               final Decoder decoder) {
        return executeWrappedCommandProtocol(binding, database, command, decoder, new IdentityTransformer());
    }

    static  T executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                               final CommandTransformer transformer) {
        return executeWrappedCommandProtocol(binding, database, command, new BsonDocumentCodec(), transformer);
    }

    static  T executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                                  final Decoder decoder, final CommandTransformer transformer) {
        return executeWrappedCommandProtocol(binding, database, command, new NoOpFieldNameValidator(), decoder, transformer);
    }

    static  T executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                               final Connection connection, final CommandTransformer transformer) {
        return executeWrappedCommandProtocol(binding, database, command, new BsonDocumentCodec(), connection, transformer);
    }

    static  T executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                               final Decoder decoder, final Connection connection,
                                               final CommandTransformer transformer) {
        notNull("binding", binding);
        return executeWrappedCommandProtocol(database, command, decoder, connection, primary(), transformer, binding.getSessionContext());
    }

    static  T executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                               final FieldNameValidator fieldNameValidator, final Decoder decoder,
                                               final Connection connection, final CommandTransformer transformer) {
        notNull("binding", binding);
        return executeWrappedCommandProtocol(database, command, fieldNameValidator, decoder, connection, primary(), transformer,
                binding.getSessionContext());
    }

    static  T executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                                  final FieldNameValidator fieldNameValidator, final Decoder decoder,
                                                  final CommandTransformer transformer) {
        ConnectionSource source = binding.getWriteConnectionSource();
        try {
            return transformer.apply(executeWrappedCommandProtocol(database, command, fieldNameValidator, decoder,
                    source, primary()), source.getServerDescription().getAddress());
        } finally {
            source.release();
        }
    }

    static BsonDocument executeWrappedCommandProtocol(final WriteBinding binding, final String database, final BsonDocument command,
                                                      final Connection connection) {
        notNull("binding", binding);
        return executeWrappedCommandProtocol(database, command, new BsonDocumentCodec(), connection, primary(),
                binding.getSessionContext());
    }

    /* Private Connection Source Helpers */

    private static  T executeWrappedCommandProtocol(final String database, final BsonDocument command,
                                                       final Decoder decoder, final ConnectionSource source,
                                                       final ReadPreference readPreference) {
        return executeWrappedCommandProtocol(database, command, new NoOpFieldNameValidator(), decoder, source, readPreference);
    }

    private static  T executeWrappedCommandProtocol(final String database, final BsonDocument command,
                                                       final FieldNameValidator fieldNameValidator, final Decoder decoder,
                                                       final ConnectionSource source, final ReadPreference readPreference) {
        Connection connection = source.getConnection();
        try {
            return executeWrappedCommandProtocol(database, command, fieldNameValidator, decoder, connection,
                    readPreference, new IdentityTransformer(), source.getSessionContext());
        } finally {
            connection.release();
        }
    }

    /* Private Connection Helpers */

    private static  T executeWrappedCommandProtocol(final String database, final BsonDocument command,
                                                       final Decoder decoder, final Connection connection,
                                                       final ReadPreference readPreference, final SessionContext sessionContext) {
        return executeWrappedCommandProtocol(database, command, new NoOpFieldNameValidator(), decoder, connection,
                readPreference, new IdentityTransformer(), sessionContext);
    }

    private static  T executeWrappedCommandProtocol(final String database, final BsonDocument command,
                                                          final Decoder decoder, final Connection connection,
                                                          final ReadPreference readPreference,
                                                          final CommandTransformer transformer, final SessionContext sessionContext) {
        return executeWrappedCommandProtocol(database, command, new NoOpFieldNameValidator(), decoder, connection,
                readPreference, transformer, sessionContext);
    }

    private static  T executeWrappedCommandProtocol(final String database, final BsonDocument command,
                                                          final FieldNameValidator fieldNameValidator, final Decoder decoder,
                                                          final Connection connection, final ReadPreference readPreference,
                                                          final CommandTransformer transformer, final SessionContext sessionContext) {

        return transformer.apply(connection.command(database, command, fieldNameValidator, readPreference, decoder, sessionContext),
                connection.getDescription().getServerAddress());
    }

    /* Async Read Binding Helpers */

    static void executeWrappedCommandProtocolAsync(final AsyncReadBinding binding,
                                                   final String database,
                                                   final BsonDocument command,
                                                   final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, new BsonDocumentCodec(), callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncReadBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final Decoder decoder,
                                                       final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, decoder, new IdentityTransformer(), callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncReadBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final CommandTransformer transformer,
                                                       final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, new BsonDocumentCodec(), transformer, callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncReadBinding binding,
                                                          final String database,
                                                          final BsonDocument command,
                                                          final Decoder decoder,
                                                          final CommandTransformer transformer,
                                                          final SingleResultCallback callback) {
        binding.getReadConnectionSource(new CommandProtocolExecutingCallback(database, command, new NoOpFieldNameValidator(),
                decoder, binding.getReadPreference(), transformer, binding.getSessionContext(), errorHandlingCallback(callback, LOGGER)));
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncReadBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final AsyncConnection connection,
                                                       final CommandTransformer transformer,
                                                       final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, new BsonDocumentCodec(), connection, transformer, callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncReadBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final Decoder decoder,
                                                       final AsyncConnection connection,
                                                       final CommandTransformer transformer,
                                                       final SingleResultCallback callback) {
        notNull("binding", binding);
        executeWrappedCommandProtocolAsync(database, command, decoder, connection, binding.getReadPreference(), transformer,
                binding.getSessionContext(), callback);
    }

    /* Async Write Binding Helpers */

    static void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                   final String database,
                                                   final BsonDocument command,
                                                   final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, new BsonDocumentCodec(), callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final Decoder decoder,
                                                       final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, decoder, new IdentityTransformer(), callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final CommandTransformer transformer,
                                                       final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, new BsonDocumentCodec(), transformer, callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                          final String database, final BsonDocument command,
                                                          final Decoder decoder,
                                                          final CommandTransformer transformer,
                                                          final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, new NoOpFieldNameValidator(), decoder, transformer, callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final Decoder decoder,
                                                       final AsyncConnection connection,
                                                       final CommandTransformer transformer,
                                                       final SingleResultCallback callback) {
        notNull("binding", binding);
        executeWrappedCommandProtocolAsync(database, command, decoder, connection, primary(), transformer, binding.getSessionContext(),
                callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final FieldNameValidator fieldNameValidator,
                                                       final Decoder decoder,
                                                       final AsyncConnection connection,
                                                       final CommandTransformer transformer,
                                                       final SingleResultCallback callback) {
        notNull("binding", binding);
        executeWrappedCommandProtocolAsync(database, command, fieldNameValidator, decoder, connection, primary(), transformer,
                binding.getSessionContext(), callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                          final String database, final BsonDocument command,
                                                          final FieldNameValidator fieldNameValidator,
                                                          final Decoder decoder,
                                                          final CommandTransformer transformer,
                                                          final SingleResultCallback callback) {
        binding.getWriteConnectionSource(new CommandProtocolExecutingCallback(database, command, fieldNameValidator, decoder,
                primary(), transformer, binding.getSessionContext(), errorHandlingCallback(callback, LOGGER)));
    }

    static void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                   final String database,
                                                   final BsonDocument command,
                                                   final AsyncConnection connection,
                                                   final SingleResultCallback callback) {
        executeWrappedCommandProtocolAsync(binding, database, command, connection, new IdentityTransformer(), callback);
    }

    static  void executeWrappedCommandProtocolAsync(final AsyncWriteBinding binding,
                                                       final String database,
                                                       final BsonDocument command,
                                                       final AsyncConnection connection,
                                                       final CommandTransformer transformer,
                                                       final SingleResultCallback callback) {
        notNull("binding", binding);
        executeWrappedCommandProtocolAsync(database, command, new BsonDocumentCodec(), connection, primary(), transformer,
                binding.getSessionContext(), callback);
    }

    /* Async Connection Helpers */
    private static  void executeWrappedCommandProtocolAsync(final String database, final BsonDocument command,
                                                                  final Decoder decoder, final AsyncConnection connection,
                                                                  final ReadPreference readPreference,
                                                                  final CommandTransformer transformer,
                                                                  final SessionContext sessionContext,
                                                                  final SingleResultCallback callback) {
        connection.commandAsync(database, command, new NoOpFieldNameValidator(), readPreference, decoder, sessionContext,
                new SingleResultCallback() {
                    @Override
                    public void onResult(final D result, final Throwable t) {
                        if (t != null) {
                            callback.onResult(null, t);
                        } else {
                            try {
                                T transformedResult = transformer.apply(result, connection.getDescription().getServerAddress());
                                callback.onResult(transformedResult, null);
                            } catch (Exception e) {
                                callback.onResult(null, e);
                            }
                        }
                    }
                });

    }

    private static  void executeWrappedCommandProtocolAsync(final String database, final BsonDocument command,
                                                                  final FieldNameValidator fieldNameValidator,
                                                                  final Decoder decoder, final AsyncConnection connection,
                                                                  final ReadPreference readPreference,
                                                                  final CommandTransformer transformer,
                                                                  final SessionContext sessionContext,
                                                                  final SingleResultCallback callback) {
        connection.commandAsync(database, command, fieldNameValidator, readPreference, decoder, sessionContext, true, null, null,
                new SingleResultCallback() {
                    @Override
                    public void onResult(final D result, final Throwable t) {
                        if (t != null) {
                            callback.onResult(null, t);
                        } else {
                            try {
                                T transformedResult = transformer.apply(result, connection.getDescription().getServerAddress());
                                callback.onResult(transformedResult, null);
                            } catch (Exception e) {
                                callback.onResult(null, e);
                            }
                        }
                    }
                });
    }

    /* Retryable write helpers */
    static  R executeRetryableCommand(final WriteBinding binding, final String database, final ReadPreference readPreference,
                                            final FieldNameValidator fieldNameValidator, final Decoder commandResultDecoder,
                                            final CommandCreator commandCreator,
                                            final CommandTransformer transformer) {
        return withReleasableConnection(binding, new CallableWithConnectionAndSource() {
            @Override
            public R call(final ConnectionSource source, final Connection connection) {
                BsonDocument command = null;
                MongoException exception;
                try {
                    command = commandCreator.create(source.getServerDescription(), connection.getDescription());
                    return transformer.apply(connection.command(database, command, fieldNameValidator, readPreference,
                            commandResultDecoder, binding.getSessionContext()), connection.getDescription().getServerAddress());
                } catch (MongoException e) {
                    exception = e;
                    if (!shouldAttemptToRetry(command, e)) {
                        if (isRetryWritesEnabled(command)) {
                            logUnableToRetry(command.getFirstKey(), e);
                        }
                        throw exception;
                    }
                } finally {
                    connection.release();
                }

                final BsonDocument originalCommand = command;
                final MongoException originalException = exception;
                return withReleasableConnection(binding, originalException, new CallableWithConnectionAndSource() {
                    @Override
                    public R call(final ConnectionSource source, final Connection connection) {
                        try {
                            if (!canRetryWrite(source.getServerDescription(), connection.getDescription(), binding.getSessionContext())) {
                                throw originalException;
                            }
                            logRetryExecute(originalCommand.getFirstKey(), originalException);
                            return transformer.apply(connection.command(database, originalCommand, fieldNameValidator,
                                    readPreference, commandResultDecoder, binding.getSessionContext()),
                                    connection.getDescription().getServerAddress());
                        } catch (MongoException e) {
                            throw originalException;
                        } finally {
                            connection.release();
                        }
                    }
                });
            }
        });
    }

    static  void executeRetryableCommand(final AsyncWriteBinding binding, final String database, final ReadPreference readPreference,
                                               final FieldNameValidator fieldNameValidator, final Decoder commandResultDecoder,
                                               final CommandCreator commandCreator, final CommandTransformer transformer,
                                               final SingleResultCallback originalCallback) {
        final SingleResultCallback errorHandlingCallback = errorHandlingCallback(originalCallback, LOGGER);
        binding.getWriteConnectionSource(new SingleResultCallback() {
            @Override
            public void onResult(final AsyncConnectionSource source, final Throwable t) {
                if (t != null) {
                    errorHandlingCallback.onResult(null, t);
                } else {
                    source.getConnection(new SingleResultCallback() {
                        @Override
                        public void onResult(final AsyncConnection connection, final Throwable t) {
                            if (t != null) {
                                releasingCallback(errorHandlingCallback, source).onResult(null, t);
                            } else {
                                try {
                                    BsonDocument command = commandCreator.create(source.getServerDescription(),
                                            connection.getDescription());
                                    connection.commandAsync(database, command, fieldNameValidator, readPreference,
                                            commandResultDecoder, binding.getSessionContext(),
                                            createCommandCallback(binding, source, connection, database, readPreference, command,
                                                    fieldNameValidator, commandResultDecoder, transformer, errorHandlingCallback));
                                } catch (Throwable t1) {
                                    releasingCallback(errorHandlingCallback, source, connection).onResult(null, t1);
                                }
                            }
                        }
                    });
                }
            }
        });
    }

    private static  SingleResultCallback createCommandCallback(final AsyncWriteBinding binding,
                                                                        final AsyncConnectionSource oldSource,
                                                                        final AsyncConnection oldConnection,
                                                                        final String database,
                                                                        final ReadPreference readPreference,
                                                                        final BsonDocument command,
                                                                        final FieldNameValidator fieldNameValidator,
                                                                        final Decoder commandResultDecoder,
                                                                        final CommandTransformer transformer,
                                                                        final SingleResultCallback callback) {
        return new SingleResultCallback() {
            @Override
            public void onResult(final T result, final Throwable originalError) {
                SingleResultCallback releasingCallback = releasingCallback(callback, oldSource, oldConnection);
                if (originalError != null) {
                    checkRetryableException(originalError, releasingCallback);
                } else {
                    try {
                        releasingCallback.onResult(transformer.apply(result, oldConnection.getDescription().getServerAddress()), null);
                    } catch (Throwable transformError) {
                        checkRetryableException(transformError, releasingCallback);
                    }
                }
            }

            private void checkRetryableException(final Throwable originalError, final SingleResultCallback releasingCallback) {
                if (!shouldAttemptToRetry(command, originalError)) {
                    if (isRetryWritesEnabled(command)) {
                        logUnableToRetry(command.getFirstKey(), originalError);
                    }
                    releasingCallback.onResult(null, originalError);
                } else {
                    oldConnection.release();
                    oldSource.release();
                    retryableCommand(originalError);
                }
            }

            private void retryableCommand(final Throwable originalError) {
                logRetryExecute(command.getFirstKey(), originalError);
                withConnection(binding, new AsyncCallableWithConnectionAndSource() {
                    @Override
                    public void call(final AsyncConnectionSource source, final AsyncConnection connection, final Throwable t) {
                        if (t != null) {
                            callback.onResult(null, originalError);
                        } else if (!canRetryWrite(source.getServerDescription(), connection.getDescription(),
                                binding.getSessionContext())) {
                            releasingCallback(callback, source, connection).onResult(null, originalError);
                        } else {
                            connection.commandAsync(database, command, fieldNameValidator, readPreference,
                                    commandResultDecoder, binding.getSessionContext(),
                                    new TransformingResultCallback(transformer,
                                            connection.getDescription().getServerAddress(),
                                            originalError, releasingCallback(callback, source, connection)));
                        }
                    }
                });
            }
        };
    }

    static class TransformingResultCallback implements SingleResultCallback {
        private final CommandTransformer transformer;
        private final ServerAddress serverAddress;
        private final Throwable originalError;
        private final SingleResultCallback callback;

        TransformingResultCallback(final CommandTransformer transformer, final ServerAddress serverAddress,
                                   final Throwable originalError, final SingleResultCallback callback) {
            this.transformer = transformer;
            this.serverAddress = serverAddress;
            this.originalError = originalError;
            this.callback = callback;
        }

        @Override
        public void onResult(final T result, final Throwable t) {
            if (t != null) {
                callback.onResult(null, t);
            } else {
                try {
                    R transformedResult = transformer.apply(result, serverAddress);
                    callback.onResult(transformedResult, null);
                } catch (Throwable transformError) {
                    callback.onResult(null, originalError);
                }
            }
        }
    }

    private static final List RETRYABLE_ERROR_CODES = asList(6, 7, 89, 91, 189, 9001, 13436, 13435, 11602, 11600, 10107);
    static boolean isRetryableException(final Throwable t) {
        if (!(t instanceof MongoException)) {
            return false;
        }

        if (t instanceof MongoSocketException || t instanceof MongoNotPrimaryException || t instanceof MongoNodeIsRecoveringException) {
            return true;
        }
        String errorMessage = t.getMessage();
        if (t instanceof MongoWriteConcernException) {
            errorMessage = ((MongoWriteConcernException) t).getWriteConcernError().getMessage();
        }
        if (errorMessage.contains("not master") || errorMessage.contains("node is recovering")) {
            return true;
        }
        return RETRYABLE_ERROR_CODES.contains(((MongoException) t).getCode());
    }

    /* Misc operation helpers */

    static void rethrowIfNotNamespaceError(final MongoCommandException e) {
        rethrowIfNotNamespaceError(e, null);
    }

    static  T rethrowIfNotNamespaceError(final MongoCommandException e, final T defaultValue) {
        if (!isNamespaceError(e)) {
            throw e;
        }
        return defaultValue;
    }

    static boolean isNamespaceError(final Throwable t) {
        if (t instanceof MongoCommandException) {
            MongoCommandException e = (MongoCommandException) t;
            return (e.getErrorMessage().contains("ns not found") || e.getErrorCode() == 26);
        } else {
            return false;
        }
    }

    private static class CommandProtocolExecutingCallback implements SingleResultCallback {
        private final String database;
        private final BsonDocument command;
        private final Decoder decoder;
        private final ReadPreference readPreference;
        private final FieldNameValidator fieldNameValidator;
        private final CommandTransformer transformer;
        private final SingleResultCallback callback;
        private final SessionContext sessionContext;

        CommandProtocolExecutingCallback(final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator,
                                         final Decoder decoder, final ReadPreference readPreference,
                                         final CommandTransformer transformer, final SessionContext sessionContext,
                                         final SingleResultCallback callback) {
            this.database = database;
            this.command = command;
            this.fieldNameValidator = fieldNameValidator;
            this.decoder = decoder;
            this.readPreference = readPreference;
            this.transformer = transformer;
            this.sessionContext = sessionContext;
            this.callback = callback;
        }

        @Override
        public void onResult(final AsyncConnectionSource source, final Throwable t) {
            if (t != null) {
                callback.onResult(null, t);
            } else {
                source.getConnection(new SingleResultCallback() {
                    @Override
                    public void onResult(final AsyncConnection connection, final Throwable t) {
                        if (t != null) {
                            callback.onResult(null, t);
                        } else {
                            final SingleResultCallback wrappedCallback = releasingCallback(callback, source, connection);
                            connection.commandAsync(database, command, fieldNameValidator, readPreference, decoder, sessionContext,
                                    new SingleResultCallback() {
                                        @Override
                                        public void onResult(final D response, final Throwable t) {
                                            if (t != null) {
                                                wrappedCallback.onResult(null, t);
                                            } else {
                                                wrappedCallback.onResult(transformer.apply(response,
                                                        connection.getDescription().getServerAddress()),
                                                        null);
                                            }
                                        }
                            });
                        }
                    }
                });
            }
        }
    }

    private static boolean shouldAttemptToRetry(@Nullable final BsonDocument command, final Throwable exception) {
        return isRetryWritesEnabled(command) && isRetryableException(exception);
    }

    private static boolean isRetryWritesEnabled(@Nullable final BsonDocument command) {
        return (command != null && (command.containsKey("txnNumber")
                || command.getFirstKey().equals("commitTransaction") || command.getFirstKey().equals("abortTransaction")));
    }

    static boolean shouldAttemptToRetry(final boolean retryWritesEnabled, final Throwable exception) {
        return retryWritesEnabled && isRetryableException(exception);
    }

    static void logRetryExecute(final String operation, final Throwable originalError) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(format("Retrying operation %s due to an error \"%s\"", operation, originalError));
        }
    }

    static void logUnableToRetry(final String operation, final Throwable originalError) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(format("Unable to retry operation %s due to error \"%s\"", operation, originalError));
        }
    }

    private CommandOperationHelper() {
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy