mongo 2.5.3 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +1 -1
- data/README.md +3 -2
- data/lib/mongo.rb +2 -2
- data/lib/mongo/address.rb +10 -2
- data/lib/mongo/address/ipv4.rb +1 -1
- data/lib/mongo/address/ipv6.rb +26 -5
- data/lib/mongo/address/unix.rb +1 -1
- data/lib/mongo/auth.rb +10 -3
- data/lib/mongo/auth/cr.rb +4 -1
- data/lib/mongo/auth/cr/conversation.rb +4 -1
- data/lib/mongo/auth/ldap.rb +1 -1
- data/lib/mongo/auth/ldap/conversation.rb +1 -1
- data/lib/mongo/auth/roles.rb +1 -1
- data/lib/mongo/auth/scram.rb +24 -7
- data/lib/mongo/auth/scram/conversation.rb +52 -19
- data/lib/mongo/auth/stringprep.rb +114 -0
- data/lib/mongo/auth/stringprep/profiles/sasl.rb +73 -0
- data/lib/mongo/auth/stringprep/tables.rb +3232 -0
- data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +174 -0
- data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +1170 -0
- data/lib/mongo/auth/user.rb +14 -3
- data/lib/mongo/auth/user/view.rb +1 -1
- data/lib/mongo/auth/x509.rb +1 -1
- data/lib/mongo/auth/x509/conversation.rb +1 -1
- data/lib/mongo/bson.rb +1 -1
- data/lib/mongo/bulk_write.rb +8 -8
- data/lib/mongo/bulk_write/combineable.rb +1 -1
- data/lib/mongo/bulk_write/ordered_combiner.rb +1 -1
- data/lib/mongo/bulk_write/result.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +4 -4
- data/lib/mongo/bulk_write/transformable.rb +1 -1
- data/lib/mongo/bulk_write/unordered_combiner.rb +1 -1
- data/lib/mongo/bulk_write/validatable.rb +1 -1
- data/lib/mongo/client.rb +115 -24
- data/lib/mongo/cluster.rb +17 -10
- data/lib/mongo/cluster/app_metadata.rb +7 -1
- data/lib/mongo/cluster/periodic_executor.rb +1 -1
- data/lib/mongo/cluster/reapers/socket_reaper.rb +1 -1
- data/lib/mongo/cluster/topology.rb +12 -2
- data/lib/mongo/cluster/topology/replica_set.rb +9 -1
- data/lib/mongo/cluster/topology/sharded.rb +1 -1
- data/lib/mongo/cluster/topology/single.rb +1 -1
- data/lib/mongo/cluster/topology/unknown.rb +1 -1
- data/lib/mongo/collection.rb +75 -19
- data/lib/mongo/collection/view.rb +1 -1
- data/lib/mongo/collection/view/aggregation.rb +1 -1
- data/lib/mongo/collection/view/builder.rb +1 -1
- data/lib/mongo/collection/view/builder/aggregation.rb +3 -3
- data/lib/mongo/collection/view/builder/find_command.rb +1 -1
- data/lib/mongo/collection/view/builder/flags.rb +1 -1
- data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/builder/modifiers.rb +1 -1
- data/lib/mongo/collection/view/builder/op_query.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +193 -17
- data/lib/mongo/collection/view/change_stream/retryable.rb +3 -20
- data/lib/mongo/collection/view/explainable.rb +1 -1
- data/lib/mongo/collection/view/immutable.rb +1 -1
- data/lib/mongo/collection/view/iterable.rb +2 -2
- data/lib/mongo/collection/view/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/readable.rb +108 -29
- data/lib/mongo/collection/view/writable.rb +3 -3
- data/lib/mongo/cursor.rb +44 -4
- data/lib/mongo/cursor/builder.rb +1 -1
- data/lib/mongo/cursor/builder/get_more_command.rb +1 -1
- data/lib/mongo/cursor/builder/kill_cursors_command.rb +1 -1
- data/lib/mongo/cursor/builder/op_get_more.rb +1 -1
- data/lib/mongo/cursor/builder/op_kill_cursors.rb +1 -1
- data/lib/mongo/database.rb +46 -3
- data/lib/mongo/database/view.rb +11 -11
- data/lib/mongo/dbref.rb +1 -1
- data/lib/mongo/error.rb +57 -1
- data/lib/mongo/error/bulk_write_error.rb +2 -2
- data/lib/mongo/error/change_stream_resumable.rb +37 -0
- data/lib/mongo/error/closed_stream.rb +1 -1
- data/lib/mongo/error/extra_file_chunk.rb +1 -1
- data/lib/mongo/error/failed_stringprep_validation.rb +38 -0
- data/lib/mongo/error/file_not_found.rb +1 -1
- data/lib/mongo/error/insufficient_iteration_count.rb +38 -0
- data/lib/mongo/error/invalid_application_name.rb +1 -1
- data/lib/mongo/error/invalid_bulk_operation.rb +1 -1
- data/lib/mongo/error/invalid_bulk_operation_type.rb +1 -1
- data/lib/mongo/error/invalid_collection_name.rb +1 -1
- data/lib/mongo/error/invalid_database_name.rb +1 -1
- data/lib/mongo/error/invalid_document.rb +1 -1
- data/lib/mongo/error/invalid_file.rb +1 -1
- data/lib/mongo/error/invalid_file_revision.rb +1 -1
- data/lib/mongo/error/invalid_min_pool_size.rb +1 -1
- data/lib/mongo/error/invalid_nonce.rb +1 -1
- data/lib/mongo/error/invalid_read_option.rb +35 -0
- data/lib/mongo/error/invalid_replacement_document.rb +1 -1
- data/lib/mongo/error/invalid_server_preference.rb +1 -1
- data/lib/mongo/error/invalid_session.rb +1 -1
- data/lib/mongo/error/invalid_signature.rb +1 -1
- data/lib/mongo/error/invalid_transaction_operation.rb +82 -0
- data/lib/mongo/error/invalid_txt_record.rb +1 -1
- data/lib/mongo/error/invalid_update_document.rb +1 -1
- data/lib/mongo/error/invalid_uri.rb +1 -1
- data/lib/mongo/error/invalid_write_concern.rb +1 -1
- data/lib/mongo/error/max_bson_size.rb +1 -1
- data/lib/mongo/error/max_message_size.rb +1 -1
- data/lib/mongo/error/mismatched_domain.rb +1 -1
- data/lib/mongo/error/missing_file_chunk.rb +1 -1
- data/lib/mongo/error/missing_resume_token.rb +1 -1
- data/lib/mongo/error/multi_index_drop.rb +1 -1
- data/lib/mongo/error/need_primary_server.rb +1 -1
- data/lib/mongo/error/no_server_available.rb +1 -1
- data/lib/mongo/error/no_srv_records.rb +1 -1
- data/lib/mongo/error/operation_failure.rb +108 -14
- data/lib/mongo/error/parser.rb +50 -1
- data/lib/mongo/error/socket_error.rb +5 -2
- data/lib/mongo/error/socket_timeout_error.rb +5 -2
- data/lib/mongo/error/unchangeable_collection_option.rb +1 -1
- data/lib/mongo/error/unexpected_chunk_length.rb +1 -1
- data/lib/mongo/error/unexpected_response.rb +1 -1
- data/lib/mongo/error/unknown_payload_type.rb +1 -1
- data/lib/mongo/error/unsupported_array_filters.rb +1 -1
- data/lib/mongo/error/unsupported_collation.rb +1 -1
- data/lib/mongo/error/unsupported_features.rb +1 -1
- data/lib/mongo/error/unsupported_message_type.rb +1 -1
- data/lib/mongo/error/write_retryable.rb +27 -0
- data/lib/mongo/event.rb +10 -9
- data/lib/mongo/event/base.rb +33 -0
- data/lib/mongo/event/description_changed.rb +2 -2
- data/lib/mongo/event/listeners.rb +1 -1
- data/lib/mongo/event/member_discovered.rb +4 -2
- data/lib/mongo/event/primary_elected.rb +2 -2
- data/lib/mongo/event/publisher.rb +1 -1
- data/lib/mongo/event/standalone_discovered.rb +2 -2
- data/lib/mongo/event/subscriber.rb +1 -1
- data/lib/mongo/grid.rb +1 -1
- data/lib/mongo/grid/file.rb +1 -1
- data/lib/mongo/grid/file/chunk.rb +3 -3
- data/lib/mongo/grid/file/info.rb +26 -3
- data/lib/mongo/grid/fs_bucket.rb +1 -1
- data/lib/mongo/grid/stream.rb +1 -1
- data/lib/mongo/grid/stream/read.rb +1 -1
- data/lib/mongo/grid/stream/write.rb +1 -1
- data/lib/mongo/index.rb +1 -1
- data/lib/mongo/index/view.rb +1 -1
- data/lib/mongo/loggable.rb +1 -1
- data/lib/mongo/logger.rb +1 -1
- data/lib/mongo/monitoring.rb +99 -62
- data/lib/mongo/monitoring/command_log_subscriber.rb +2 -2
- data/lib/mongo/monitoring/event.rb +2 -1
- data/lib/mongo/monitoring/event/command_failed.rb +19 -6
- data/lib/mongo/monitoring/event/command_started.rb +14 -3
- data/lib/mongo/monitoring/event/command_succeeded.rb +5 -3
- data/lib/mongo/monitoring/event/secure.rb +1 -1
- data/lib/mongo/monitoring/event/server_closed.rb +2 -2
- data/lib/mongo/monitoring/event/server_description_changed.rb +2 -2
- data/lib/mongo/monitoring/event/server_opening.rb +11 -2
- data/lib/mongo/monitoring/event/topology_changed.rb +13 -2
- data/lib/mongo/monitoring/event/topology_closed.rb +2 -2
- data/lib/mongo/monitoring/event/topology_opening.rb +11 -2
- data/lib/mongo/monitoring/publishable.rb +10 -6
- data/lib/mongo/monitoring/sdam_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/server_closed_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/server_opening_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
- data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +1 -1
- data/lib/mongo/operation/aggregate/op_msg.rb +3 -0
- data/lib/mongo/operation/create/op_msg.rb +9 -0
- data/lib/mongo/operation/create_index/op_msg.rb +9 -0
- data/lib/mongo/operation/create_user/command.rb +1 -1
- data/lib/mongo/operation/create_user/op_msg.rb +10 -1
- data/lib/mongo/operation/delete/op_msg.rb +3 -0
- data/lib/mongo/operation/distinct/op_msg.rb +9 -0
- data/lib/mongo/operation/drop/op_msg.rb +9 -0
- data/lib/mongo/operation/drop_database/op_msg.rb +9 -0
- data/lib/mongo/operation/drop_index/op_msg.rb +9 -0
- data/lib/mongo/operation/explain/op_msg.rb +3 -0
- data/lib/mongo/operation/find/op_msg.rb +3 -0
- data/lib/mongo/operation/get_more.rb +1 -1
- data/lib/mongo/operation/get_more/command.rb +1 -1
- data/lib/mongo/operation/get_more/legacy.rb +1 -1
- data/lib/mongo/operation/get_more/op_msg.rb +3 -0
- data/lib/mongo/operation/indexes/op_msg.rb +3 -0
- data/lib/mongo/operation/indexes/result.rb +1 -1
- data/lib/mongo/operation/insert/bulk_result.rb +32 -2
- data/lib/mongo/operation/insert/op_msg.rb +3 -0
- data/lib/mongo/operation/insert/result.rb +1 -1
- data/lib/mongo/operation/kill_cursors/op_msg.rb +9 -0
- data/lib/mongo/operation/list_collections/op_msg.rb +3 -0
- data/lib/mongo/operation/list_collections/result.rb +5 -1
- data/lib/mongo/operation/map_reduce/op_msg.rb +3 -0
- data/lib/mongo/operation/map_reduce/result.rb +1 -1
- data/lib/mongo/operation/parallel_scan/op_msg.rb +3 -0
- data/lib/mongo/operation/remove_user/op_msg.rb +9 -0
- data/lib/mongo/operation/result.rb +27 -14
- data/lib/mongo/operation/shared/executable.rb +1 -0
- data/lib/mongo/operation/shared/sessions_supported.rb +78 -7
- data/lib/mongo/operation/shared/specifiable.rb +18 -2
- data/lib/mongo/operation/shared/write_concern_supported.rb +1 -1
- data/lib/mongo/operation/update/op_msg.rb +3 -0
- data/lib/mongo/operation/update_user/command.rb +1 -1
- data/lib/mongo/operation/update_user/op_msg.rb +10 -1
- data/lib/mongo/operation/users_info/op_msg.rb +3 -0
- data/lib/mongo/options.rb +1 -1
- data/lib/mongo/options/mapper.rb +1 -1
- data/lib/mongo/options/redacted.rb +1 -1
- data/lib/mongo/protocol/bit_vector.rb +1 -1
- data/lib/mongo/protocol/compressed.rb +1 -1
- data/lib/mongo/protocol/delete.rb +1 -1
- data/lib/mongo/protocol/get_more.rb +7 -7
- data/lib/mongo/protocol/insert.rb +1 -1
- data/lib/mongo/protocol/kill_cursors.rb +1 -1
- data/lib/mongo/protocol/message.rb +5 -5
- data/lib/mongo/protocol/msg.rb +9 -7
- data/lib/mongo/protocol/query.rb +1 -1
- data/lib/mongo/protocol/registry.rb +1 -1
- data/lib/mongo/protocol/reply.rb +10 -10
- data/lib/mongo/protocol/serializers.rb +1 -1
- data/lib/mongo/protocol/update.rb +1 -1
- data/lib/mongo/retryable.rb +22 -14
- data/lib/mongo/server.rb +1 -1
- data/lib/mongo/server/connectable.rb +1 -1
- data/lib/mongo/server/connection.rb +16 -4
- data/lib/mongo/server/connection_pool.rb +1 -1
- data/lib/mongo/server/connection_pool/queue.rb +1 -1
- data/lib/mongo/server/context.rb +1 -1
- data/lib/mongo/server/description.rb +14 -2
- data/lib/mongo/server/description/features.rb +10 -9
- data/lib/mongo/server/description/inspector.rb +1 -1
- data/lib/mongo/server/description/inspector/description_changed.rb +1 -1
- data/lib/mongo/server/description/inspector/member_discovered.rb +1 -1
- data/lib/mongo/server/description/inspector/primary_elected.rb +1 -1
- data/lib/mongo/server/description/inspector/standalone_discovered.rb +1 -1
- data/lib/mongo/server/monitor.rb +15 -3
- data/lib/mongo/server/monitor/connection.rb +1 -1
- data/lib/mongo/server_selector.rb +1 -1
- data/lib/mongo/server_selector/nearest.rb +1 -1
- data/lib/mongo/server_selector/primary.rb +1 -1
- data/lib/mongo/server_selector/primary_preferred.rb +1 -1
- data/lib/mongo/server_selector/secondary.rb +1 -1
- data/lib/mongo/server_selector/secondary_preferred.rb +1 -1
- data/lib/mongo/server_selector/selectable.rb +7 -2
- data/lib/mongo/session.rb +389 -12
- data/lib/mongo/session/server_session.rb +7 -2
- data/lib/mongo/session/session_pool.rb +1 -1
- data/lib/mongo/socket.rb +1 -1
- data/lib/mongo/socket/ssl.rb +1 -1
- data/lib/mongo/socket/tcp.rb +1 -1
- data/lib/mongo/socket/unix.rb +1 -1
- data/lib/mongo/uri.rb +6 -4
- data/lib/mongo/uri/srv_protocol.rb +1 -1
- data/lib/mongo/version.rb +2 -2
- data/lib/mongo/write_concern.rb +1 -1
- data/lib/mongo/write_concern/acknowledged.rb +1 -1
- data/lib/mongo/write_concern/normalizable.rb +1 -1
- data/lib/mongo/write_concern/unacknowledged.rb +1 -1
- data/mongo.gemspec +4 -1
- data/spec/atlas/atlas_connectivity_spec.rb +54 -0
- data/spec/integration/bulk_insert_spec.rb +78 -0
- data/spec/integration/change_stream_spec.rb +365 -0
- data/spec/integration/command_monitoring_spec.rb +92 -0
- data/spec/lite_spec_helper.rb +63 -0
- data/spec/mongo/address/ipv6_spec.rb +29 -1
- data/spec/mongo/address_spec.rb +34 -0
- data/spec/mongo/auth/scram/conversation_spec.rb +326 -120
- data/spec/mongo/auth/scram/negotiation_spec.rb +574 -0
- data/spec/mongo/auth/scram_spec.rb +107 -38
- data/spec/mongo/auth/stringprep/profiles/sasl_spec.rb +113 -0
- data/spec/mongo/auth/stringprep_spec.rb +188 -0
- data/spec/mongo/auth/user/view_spec.rb +5 -2
- data/spec/mongo/auth/user_spec.rb +1 -1
- data/spec/mongo/bulk_write/result_spec.rb +120 -0
- data/spec/mongo/bulk_write_spec.rb +42 -2
- data/spec/mongo/client_spec.rb +121 -9
- data/spec/mongo/cluster/app_metadata_spec.rb +14 -1
- data/spec/mongo/cluster/topology_spec.rb +1 -23
- data/spec/mongo/collection/view/change_stream_spec.rb +62 -180
- data/spec/mongo/collection_spec.rb +45 -12
- data/spec/mongo/cursor/builder/get_more_command_spec.rb +7 -7
- data/spec/mongo/cursor_spec.rb +2 -2
- data/spec/mongo/database_spec.rb +3 -3
- data/spec/mongo/docs_examples_spec.rb +194 -0
- data/spec/mongo/error/operation_failure_spec.rb +152 -0
- data/spec/mongo/error/parser_spec.rb +127 -0
- data/spec/mongo/grid/fs_bucket_spec.rb +32 -0
- data/spec/mongo/grid/stream/write_spec.rb +40 -1
- data/spec/mongo/monitoring/event/command_failed_spec.rb +30 -0
- data/spec/mongo/monitoring/event/command_started_spec.rb +26 -4
- data/spec/mongo/monitoring/event/command_succeeded_spec.rb +29 -7
- data/spec/mongo/monitoring_spec.rb +28 -3
- data/spec/mongo/protocol/get_more_spec.rb +2 -2
- data/spec/mongo/retryable_spec.rb +252 -34
- data/spec/mongo/retryable_writes_spec.rb +468 -544
- data/spec/mongo/server/connection_spec.rb +5 -5
- data/spec/mongo/server/description_spec.rb +23 -6
- data/spec/mongo/session/server_session_spec.rb +2 -2
- data/spec/mongo/session/session_pool_spec.rb +2 -2
- data/spec/mongo/transactions_examples_spec.rb +227 -0
- data/spec/mongo/transactions_spec.rb +44 -0
- data/spec/spec_helper.rb +135 -49
- data/spec/spec_tests/change_streams_spec.rb +42 -0
- data/spec/{mongo → spec_tests}/command_monitoring_spec.rb +8 -2
- data/spec/{mongo → spec_tests}/connection_string_spec.rb +1 -1
- data/spec/{mongo → spec_tests}/crud_spec.rb +5 -7
- data/spec/{mongo → spec_tests}/dns_seedlist_discovery_spec.rb +1 -1
- data/spec/{mongo → spec_tests}/gridfs_spec.rb +0 -0
- data/spec/{mongo → spec_tests}/max_staleness_spec.rb +0 -0
- data/spec/spec_tests/retryable_writes_spec.rb +78 -0
- data/spec/{mongo → spec_tests}/sdam_monitoring_spec.rb +4 -3
- data/spec/{mongo → spec_tests}/sdam_spec.rb +7 -7
- data/spec/{mongo → spec_tests}/server_selection_rtt_spec.rb +0 -0
- data/spec/{mongo → spec_tests}/server_selection_spec.rb +0 -0
- data/spec/support/authorization.rb +18 -6
- data/spec/support/change_streams.rb +265 -0
- data/spec/support/change_streams/operation.rb +62 -0
- data/spec/support/change_streams_tests/change-streams-errors.yml +53 -0
- data/spec/support/change_streams_tests/change-streams.yml +299 -0
- data/spec/support/command_monitoring.rb +1 -1
- data/spec/support/command_monitoring/bulkWrite.yml +4 -28
- data/spec/support/command_monitoring/command.yml +19 -0
- data/spec/support/command_monitoring/find.yml +17 -19
- data/spec/support/command_monitoring/insertMany.yml +2 -8
- data/spec/support/command_monitoring/unacknowledgedBulkWrite.yml +34 -0
- data/spec/support/connection_string.rb +1 -1
- data/spec/support/constraints.rb +56 -0
- data/spec/support/crud.rb +9 -4
- data/spec/support/crud/read.rb +24 -3
- data/spec/support/crud/write.rb +7 -2
- data/spec/support/crud_tests/read/count-collation.yml +12 -2
- data/spec/support/crud_tests/read/count.yml +43 -5
- data/spec/support/gridfs.rb +1 -1
- data/spec/support/primary_socket.rb +21 -0
- data/spec/support/retryable_writes_tests/bulkWrite-serverErrors.yml +90 -0
- data/spec/support/retryable_writes_tests/bulkWrite.yml +99 -1
- data/spec/support/retryable_writes_tests/deleteOne-serverErrors.yml +50 -0
- data/spec/support/retryable_writes_tests/deleteOne.yml +10 -1
- data/spec/support/retryable_writes_tests/findOneAndDelete-serverErrors.yml +50 -0
- data/spec/support/retryable_writes_tests/findOneAndDelete.yml +39 -30
- data/spec/support/retryable_writes_tests/findOneAndReplace-serverErrors.yml +54 -0
- data/spec/support/retryable_writes_tests/findOneAndReplace.yml +9 -0
- data/spec/support/retryable_writes_tests/findOneAndUpdate-serverErrors.yml +54 -0
- data/spec/support/retryable_writes_tests/findOneAndUpdate.yml +9 -0
- data/spec/support/retryable_writes_tests/insertMany-serverErrors.yml +59 -0
- data/spec/support/retryable_writes_tests/insertMany.yml +11 -6
- data/spec/support/retryable_writes_tests/insertOne-serverErrors.yml +471 -0
- data/spec/support/retryable_writes_tests/insertOne.yml +9 -0
- data/spec/support/retryable_writes_tests/replaceOne-serverErrors.yml +58 -0
- data/spec/support/retryable_writes_tests/replaceOne.yml +9 -0
- data/spec/support/retryable_writes_tests/updateOne-serverErrors.yml +58 -0
- data/spec/support/retryable_writes_tests/updateOne.yml +71 -53
- data/spec/support/sdam/rs/normalize_case_me.yml +100 -0
- data/spec/support/sdam/sharded/compatible.yml +38 -0
- data/spec/support/sdam/sharded/mongos_disconnect.yml +9 -3
- data/spec/support/sdam/sharded/multiple_mongoses.yml +6 -2
- data/spec/support/sdam/sharded/non_mongos_removed.yml +6 -2
- data/spec/support/sdam/sharded/too_new.yml +36 -0
- data/spec/support/sdam/sharded/too_old.yml +36 -0
- data/spec/support/sdam/single/compatible.yml +26 -0
- data/spec/support/sdam/single/direct_connection_external_ip.yml +3 -1
- data/spec/support/sdam/single/direct_connection_mongos.yml +3 -1
- data/spec/support/sdam/single/direct_connection_rsarbiter.yml +3 -1
- data/spec/support/sdam/single/direct_connection_rsprimary.yml +3 -1
- data/spec/support/sdam/single/direct_connection_rssecondary.yml +3 -1
- data/spec/support/sdam/single/direct_connection_slave.yml +3 -1
- data/spec/support/sdam/single/direct_connection_standalone.yml +3 -1
- data/spec/support/sdam/single/not_ok_response.yml +6 -2
- data/spec/support/sdam/single/standalone_removed.yml +3 -1
- data/spec/support/sdam/single/too_new.yml +26 -0
- data/spec/support/sdam/single/too_old.yml +24 -0
- data/spec/support/shared/session.rb +107 -0
- data/spec/support/transactions.rb +391 -0
- data/spec/support/transactions/operation.rb +373 -0
- data/spec/support/transactions_tests/abort.yml +403 -0
- data/spec/support/transactions_tests/bulk.yml +267 -0
- data/spec/support/transactions_tests/causal-consistency.yml +173 -0
- data/spec/support/transactions_tests/commit.yml +593 -0
- data/spec/support/transactions_tests/delete.yml +184 -0
- data/spec/support/transactions_tests/error-labels.yml +948 -0
- data/spec/support/transactions_tests/errors.yml +125 -0
- data/spec/support/transactions_tests/findOneAndDelete.yml +126 -0
- data/spec/support/transactions_tests/findOneAndReplace.yml +140 -0
- data/spec/support/transactions_tests/findOneAndUpdate.yml +228 -0
- data/spec/support/transactions_tests/insert.yml +264 -0
- data/spec/support/transactions_tests/isolation.yml +125 -0
- data/spec/support/transactions_tests/read-pref.yml +340 -0
- data/spec/support/transactions_tests/reads.yml +298 -0
- data/spec/support/transactions_tests/retryable-abort.yml +1292 -0
- data/spec/support/transactions_tests/retryable-commit.yml +1332 -0
- data/spec/support/transactions_tests/retryable-writes.yml +208 -0
- data/spec/support/transactions_tests/run-command.yml +189 -0
- data/spec/support/transactions_tests/transaction-options.yml +877 -0
- data/spec/support/transactions_tests/update.yml +246 -0
- data/spec/support/transactions_tests/write-concern.yml +236 -0
- metadata +494 -359
- metadata.gz.sig +0 -0
- data/lib/csasl/csasl.bundle +0 -0
data/lib/mongo/retryable.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2015 MongoDB, Inc.
|
1
|
+
# Copyright (C) 2015-2018 MongoDB, Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -30,6 +30,7 @@ module Mongo
|
|
30
30
|
#
|
31
31
|
# @note This only retries read operations on socket errors.
|
32
32
|
#
|
33
|
+
# @param [ Mongo::Session ] session The session that the operation is being run on.
|
33
34
|
# @param [ Proc ] block The block to execute.
|
34
35
|
#
|
35
36
|
# @yieldparam [ Server ] server The server to which the write should be sent.
|
@@ -37,18 +38,18 @@ module Mongo
|
|
37
38
|
# @return [ Result ] The result of the operation.
|
38
39
|
#
|
39
40
|
# @since 2.1.0
|
40
|
-
def read_with_retry
|
41
|
+
def read_with_retry(session = nil)
|
41
42
|
attempt = 0
|
42
43
|
begin
|
43
44
|
attempt += 1
|
44
45
|
yield
|
45
46
|
rescue Error::SocketError, Error::SocketTimeoutError => e
|
46
|
-
raise(e) if attempt > cluster.max_read_retries
|
47
|
+
raise(e) if attempt > cluster.max_read_retries || (session && session.in_transaction?)
|
47
48
|
log_retry(e)
|
48
49
|
cluster.scan!
|
49
50
|
retry
|
50
51
|
rescue Error::OperationFailure => e
|
51
|
-
if cluster.sharded? && e.retryable?
|
52
|
+
if cluster.sharded? && e.retryable? && !(session && session.in_transaction?)
|
52
53
|
raise(e) if attempt > cluster.max_read_retries
|
53
54
|
log_retry(e)
|
54
55
|
sleep(cluster.read_retry_interval)
|
@@ -93,28 +94,32 @@ module Mongo
|
|
93
94
|
# @note This only retries operations on not master failures, since it is
|
94
95
|
# the only case we can be sure a partial write did not already occur.
|
95
96
|
#
|
97
|
+
# @param [ true | false ] ending_transaction True if the write operation is abortTransaction or
|
98
|
+
# commitTransaction, false otherwise.
|
96
99
|
# @param [ Proc ] block The block to execute.
|
97
100
|
#
|
98
101
|
# @return [ Result ] The result of the operation.
|
99
102
|
#
|
100
103
|
# @since 2.1.0
|
101
|
-
def write_with_retry(session, write_concern, &block)
|
102
|
-
unless retry_write_allowed?(session, write_concern)
|
103
|
-
return legacy_write_with_retry(&block)
|
104
|
+
def write_with_retry(session, write_concern, ending_transaction = false, &block)
|
105
|
+
unless retry_write_allowed?(session, write_concern) || ending_transaction
|
106
|
+
return legacy_write_with_retry(nil, session, &block)
|
104
107
|
end
|
105
108
|
|
106
109
|
server = cluster.next_primary
|
107
|
-
|
108
|
-
|
110
|
+
|
111
|
+
unless server.retry_writes? || ending_transaction
|
112
|
+
return legacy_write_with_retry(server, session, &block)
|
109
113
|
end
|
110
114
|
|
111
115
|
begin
|
112
|
-
txn_num = session.next_txn_num
|
116
|
+
txn_num = session.in_transaction? ? session.txn_num : session.next_txn_num
|
113
117
|
yield(server, txn_num)
|
114
118
|
rescue Error::SocketError, Error::SocketTimeoutError => e
|
119
|
+
raise e if session.in_transaction? && !ending_transaction
|
115
120
|
retry_write(e, txn_num, &block)
|
116
121
|
rescue Error::OperationFailure => e
|
117
|
-
raise e
|
122
|
+
raise e if (session.in_transaction? && !ending_transaction) || !e.write_retryable?
|
118
123
|
retry_write(e, txn_num, &block)
|
119
124
|
end
|
120
125
|
end
|
@@ -123,7 +128,7 @@ module Mongo
|
|
123
128
|
|
124
129
|
def retry_write_allowed?(session, write_concern)
|
125
130
|
session && session.retry_writes? &&
|
126
|
-
(write_concern.nil? || write_concern.acknowledged?)
|
131
|
+
(write_concern.nil? || write_concern.acknowledged?) or false
|
127
132
|
end
|
128
133
|
|
129
134
|
def retry_write(original_error, txn_num, &block)
|
@@ -143,7 +148,10 @@ module Mongo
|
|
143
148
|
raise original_error
|
144
149
|
end
|
145
150
|
|
146
|
-
def legacy_write_with_retry(server = nil)
|
151
|
+
def legacy_write_with_retry(server = nil, session = nil)
|
152
|
+
# This is the pre-session retry logic, and is not subject to
|
153
|
+
# current retryable write specifications.
|
154
|
+
# In particular it does not retry on SocketError and SocketTimeoutError.
|
147
155
|
attempt = 0
|
148
156
|
begin
|
149
157
|
attempt += 1
|
@@ -151,7 +159,7 @@ module Mongo
|
|
151
159
|
rescue Error::OperationFailure => e
|
152
160
|
server = nil
|
153
161
|
raise(e) if attempt > Cluster::MAX_WRITE_RETRIES
|
154
|
-
if e.write_retryable?
|
162
|
+
if e.write_retryable? && !(session && session.in_transaction?)
|
155
163
|
log_retry(e)
|
156
164
|
cluster.scan!
|
157
165
|
retry
|
data/lib/mongo/server.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014-
|
1
|
+
# Copyright (C) 2014-2018 MongoDB, Inc.
|
2
2
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -135,7 +135,7 @@ module Mongo
|
|
135
135
|
# @param [ Array<Message> ] messages The messages to dispatch.
|
136
136
|
# @param [ Integer ] operation_id The operation id to link messages.
|
137
137
|
#
|
138
|
-
# @return [ Protocol::Message ] The reply if needed.
|
138
|
+
# @return [ Protocol::Message | nil ] The reply if needed.
|
139
139
|
#
|
140
140
|
# @since 2.0.0
|
141
141
|
def dispatch(messages, operation_id = nil)
|
@@ -237,14 +237,26 @@ module Mongo
|
|
237
237
|
min_wire_version = response[Description::MIN_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
|
238
238
|
max_wire_version = response[Description::MAX_WIRE_VERSION] || Description::LEGACY_WIRE_VERSION
|
239
239
|
features = Description::Features.new(min_wire_version..max_wire_version)
|
240
|
-
|
240
|
+
|
241
|
+
if response["ok"] == 1
|
242
|
+
@auth_mechanism = if response['saslSupportedMechs']
|
243
|
+
if response['saslSupportedMechs'].include?(Mongo::Auth::SCRAM::SCRAM_SHA_256_MECHANISM)
|
244
|
+
:scram256
|
245
|
+
else
|
246
|
+
:scram
|
247
|
+
end
|
248
|
+
elsif features.scram_sha_1_enabled? || @server.features.scram_sha_1_enabled?
|
249
|
+
:scram
|
250
|
+
else
|
251
|
+
:mongodb_cr
|
252
|
+
end
|
253
|
+
end
|
241
254
|
end
|
242
255
|
end
|
243
256
|
|
244
257
|
def authenticate!
|
245
258
|
if options[:user] || options[:auth_mech]
|
246
259
|
user = Auth::User.new(Options::Redacted.new(:auth_mech => default_mechanism, :client_key => @client_key).merge(options))
|
247
|
-
|
248
260
|
@server.handle_auth_failure! do
|
249
261
|
reply = Auth.get(user).login(self)
|
250
262
|
@client_key ||= user.send(:client_key) if user.mechanism == :scram
|
data/lib/mongo/server/context.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014-
|
1
|
+
# Copyright (C) 2014-2018 MongoDB, Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the 'License');
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -461,6 +461,18 @@ module Mongo
|
|
461
461
|
@passives ||= (config[PASSIVES] || []).map { |s| s.downcase }
|
462
462
|
end
|
463
463
|
|
464
|
+
# Get the address of the primary host.
|
465
|
+
#
|
466
|
+
# @example Get the address of the primary.
|
467
|
+
# description.primary_host
|
468
|
+
#
|
469
|
+
# @return [ String | nil ] The address of the primary.
|
470
|
+
#
|
471
|
+
# @since 2.6.0
|
472
|
+
def primary_host
|
473
|
+
config[PRIMARY_HOST] && config[PRIMARY_HOST].downcase
|
474
|
+
end
|
475
|
+
|
464
476
|
# Will return true if the server is a primary.
|
465
477
|
#
|
466
478
|
# @example Is the server a primary?
|
@@ -471,7 +483,7 @@ module Mongo
|
|
471
483
|
# @since 2.0.0
|
472
484
|
def primary?
|
473
485
|
!!config[PRIMARY] &&
|
474
|
-
(
|
486
|
+
(primary_host.nil? || primary_host == address.to_s) &&
|
475
487
|
!replica_set_name.nil?
|
476
488
|
end
|
477
489
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014 MongoDB, Inc.
|
1
|
+
# Copyright (C) 2014-2018 MongoDB, Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the 'License');
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -25,6 +25,8 @@ module Mongo
|
|
25
25
|
#
|
26
26
|
# @since 2.0.0
|
27
27
|
MAPPINGS = {
|
28
|
+
:transactions => 7,
|
29
|
+
:scram_sha_256 => 7,
|
28
30
|
:array_filters => 6,
|
29
31
|
:op_msg => 6,
|
30
32
|
:sessions => 6,
|
@@ -53,19 +55,18 @@ module Mongo
|
|
53
55
|
# The wire protocol versions that this version of the driver supports.
|
54
56
|
#
|
55
57
|
# @since 2.0.0
|
56
|
-
DRIVER_WIRE_VERSIONS = (2..
|
58
|
+
DRIVER_WIRE_VERSIONS = (2..7).freeze
|
57
59
|
|
58
60
|
# Create the methods for each mapping to tell if they are supported.
|
59
61
|
#
|
60
62
|
# @since 2.0.0
|
61
63
|
MAPPINGS.each do |name, version|
|
62
|
-
|
63
|
-
# Determine whether or not the feature is enabled.
|
64
|
+
# Determine whether or not the feature is supported.
|
64
65
|
#
|
65
66
|
# @example Is a feature enabled?
|
66
67
|
# features.list_collections_enabled?
|
67
68
|
#
|
68
|
-
# @return [ true, false ]
|
69
|
+
# @return [ true, false ] Whether the feature is supported.
|
69
70
|
#
|
70
71
|
# @since 2.0.0
|
71
72
|
define_method("#{name}_enabled?") do
|
@@ -91,14 +92,14 @@ module Mongo
|
|
91
92
|
@address = address
|
92
93
|
end
|
93
94
|
|
94
|
-
# Check that there is an overlap between the driver supported wire
|
95
|
-
# and the server wire version range.
|
95
|
+
# Check that there is an overlap between the driver supported wire
|
96
|
+
# version range and the server wire version range.
|
96
97
|
#
|
97
98
|
# @example Verify the wire version overlap.
|
98
99
|
# features.check_driver_support!
|
99
100
|
#
|
100
|
-
# @raise [ Error::UnsupportedFeatures ] If the wire version range is
|
101
|
-
# by the driver.
|
101
|
+
# @raise [ Error::UnsupportedFeatures ] If the wire version range is
|
102
|
+
# not covered by the driver.
|
102
103
|
#
|
103
104
|
# @since 2.5.1
|
104
105
|
def check_driver_support!
|
data/lib/mongo/server/monitor.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014-
|
1
|
+
# Copyright (C) 2014-2018 MongoDB Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -63,9 +63,19 @@ module Mongo
|
|
63
63
|
# of the connection.
|
64
64
|
def_delegators :connection, :compressor
|
65
65
|
|
66
|
-
#
|
66
|
+
# Perform a check of the server with throttling, and update
|
67
|
+
# the server's description.
|
67
68
|
#
|
68
|
-
#
|
69
|
+
# If the server was checked less than MIN_SCAN_FREQUENCY seconds
|
70
|
+
# ago, sleep until MIN_SCAN_FREQUENCY seconds have passed since the last
|
71
|
+
# check. Then perform the check which involves running isMaster
|
72
|
+
# on the server being monitored and updating the server description
|
73
|
+
# as a result.
|
74
|
+
#
|
75
|
+
# @note If the system clock is set to a time in the past, this method
|
76
|
+
# can sleep for a very long time.
|
77
|
+
#
|
78
|
+
# @example Run a scan.
|
69
79
|
# monitor.scan!
|
70
80
|
#
|
71
81
|
# @return [ Description ] The updated description.
|
@@ -179,6 +189,8 @@ module Mongo
|
|
179
189
|
end
|
180
190
|
end
|
181
191
|
|
192
|
+
# @note If the system clock is set to a time in the past, this method
|
193
|
+
# can sleep for a very long time.
|
182
194
|
def throttle_scan_frequency!
|
183
195
|
if @last_scan
|
184
196
|
difference = (Time.now - @last_scan)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014-
|
1
|
+
# Copyright (C) 2014-2018 MongoDB, Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -102,7 +102,12 @@ module Mongo
|
|
102
102
|
servers = candidates(cluster)
|
103
103
|
if servers && !servers.compact.empty?
|
104
104
|
server = servers.first
|
105
|
-
|
105
|
+
# HACK: all servers in a topology must satisfy wire protocol
|
106
|
+
# constraints. There is probably a better implementation than
|
107
|
+
# checking all servers here
|
108
|
+
cluster.servers.each do |a_server|
|
109
|
+
a_server.check_driver_support!
|
110
|
+
end
|
106
111
|
return server
|
107
112
|
end
|
108
113
|
cluster.scan!
|
data/lib/mongo/session.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2017 MongoDB, Inc.
|
1
|
+
# Copyright (C) 2017-2018 MongoDB, Inc.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -23,6 +23,7 @@ module Mongo
|
|
23
23
|
# @since 2.5.0
|
24
24
|
class Session
|
25
25
|
extend Forwardable
|
26
|
+
include Retryable
|
26
27
|
|
27
28
|
# Get the options for this session.
|
28
29
|
#
|
@@ -32,7 +33,7 @@ module Mongo
|
|
32
33
|
# Get the cluster through which this session was created.
|
33
34
|
#
|
34
35
|
# @since 2.5.1
|
35
|
-
attr_reader :
|
36
|
+
attr_reader :client
|
36
37
|
|
37
38
|
# The cluster time for this session.
|
38
39
|
#
|
@@ -44,6 +45,11 @@ module Mongo
|
|
44
45
|
# @since 2.5.0
|
45
46
|
attr_reader :operation_time
|
46
47
|
|
48
|
+
# The options for the transaction currently being executed on the session.
|
49
|
+
#
|
50
|
+
# @since 2.6.0
|
51
|
+
attr_reader :txn_options
|
52
|
+
|
47
53
|
# Error message indicating that the session was retrieved from a client with a different cluster than that of the
|
48
54
|
# client through which it is currently being used.
|
49
55
|
#
|
@@ -62,21 +68,62 @@ module Mongo
|
|
62
68
|
# @since 2.5.0
|
63
69
|
SESSIONS_NOT_SUPPORTED = 'Sessions are not supported by the connected servers.'.freeze
|
64
70
|
|
71
|
+
# The state of a session in which the last operation was not related to any transaction or no
|
72
|
+
# operations have yet occurred.
|
73
|
+
#
|
74
|
+
# @since 2.6.0
|
75
|
+
NO_TRANSACTION_STATE = :no_transaction
|
76
|
+
|
77
|
+
# The state of a session in which a user has initiated a transaction but no operations within
|
78
|
+
# the transactions have occurred yet.
|
79
|
+
#
|
80
|
+
# @since 2.6.0
|
81
|
+
STARTING_TRANSACTION_STATE = :starting_transaction
|
82
|
+
|
83
|
+
# The state of a session in which a transaction has been started and at least one operation has
|
84
|
+
# occurred, but the transaction has not yet been committed or aborted.
|
85
|
+
#
|
86
|
+
# @since 2.6.0
|
87
|
+
TRANSACTION_IN_PROGRESS_STATE = :transaction_in_progress
|
88
|
+
|
89
|
+
# The state of a session in which the last operation executed was a transaction commit.
|
90
|
+
#
|
91
|
+
# @since 2.6.0
|
92
|
+
TRANSACTION_COMMITTED_STATE = :transaction_committed
|
93
|
+
|
94
|
+
# The state of a session in which the last operation executed was a transaction abort.
|
95
|
+
#
|
96
|
+
# @since 2.6.0
|
97
|
+
TRANSACTION_ABORTED_STATE = :transaction_aborted
|
98
|
+
|
99
|
+
UNLABELED_WRITE_CONCERN_CODES = [
|
100
|
+
79, # UnknownReplWriteConcern
|
101
|
+
100, # CannotSatisfyWriteConcern,
|
102
|
+
].freeze
|
103
|
+
|
65
104
|
# Initialize a Session.
|
66
105
|
#
|
67
106
|
# @example
|
68
|
-
# Session.new(server_session,
|
107
|
+
# Session.new(server_session, client, options)
|
69
108
|
#
|
70
109
|
# @param [ ServerSession ] server_session The server session this session is associated with.
|
71
|
-
# @param [
|
110
|
+
# @param [ Client ] client The client through which this session is created.
|
72
111
|
# @param [ Hash ] options The options for this session.
|
73
112
|
#
|
74
113
|
# @since 2.5.0
|
75
|
-
def initialize(server_session,
|
114
|
+
def initialize(server_session, client, options = {})
|
76
115
|
@server_session = server_session
|
77
|
-
|
116
|
+
|
117
|
+
# Because the read preference will need to be inserted into a command as a string, we convert
|
118
|
+
# it from a symbol immediately upon receiving it.
|
119
|
+
if options[:read_preference] && options[:read_preference][:mode]
|
120
|
+
options[:read_preference][:mode] = options[:read_preference][:mode].to_s
|
121
|
+
end
|
122
|
+
|
123
|
+
@client = client.use(:admin)
|
78
124
|
@options = options.dup.freeze
|
79
125
|
@cluster_time = nil
|
126
|
+
@state = NO_TRANSACTION_STATE
|
80
127
|
end
|
81
128
|
|
82
129
|
# Get a formatted string for use in inspection.
|
@@ -100,8 +147,9 @@ module Mongo
|
|
100
147
|
#
|
101
148
|
# @since 2.5.0
|
102
149
|
def end_session
|
103
|
-
if !ended? && @
|
104
|
-
|
150
|
+
if !ended? && @client
|
151
|
+
abort_transaction if within_states?(TRANSACTION_IN_PROGRESS_STATE) rescue Mongo::Error
|
152
|
+
@client.cluster.session_pool.checkin(@server_session)
|
105
153
|
end
|
106
154
|
ensure
|
107
155
|
@server_session = nil
|
@@ -119,6 +167,20 @@ module Mongo
|
|
119
167
|
@server_session.nil?
|
120
168
|
end
|
121
169
|
|
170
|
+
# Add the autocommit field to a command document if applicable.
|
171
|
+
#
|
172
|
+
# @example
|
173
|
+
# session.add_autocommit!(cmd)
|
174
|
+
#
|
175
|
+
# @return [ Hash, BSON::Document ] The command document.
|
176
|
+
#
|
177
|
+
# @since 2.6.0
|
178
|
+
def add_autocommit!(command)
|
179
|
+
command.tap do |c|
|
180
|
+
c[:autocommit] = false if in_transaction?
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
122
184
|
# Add this session's id to a command document.
|
123
185
|
#
|
124
186
|
# @example
|
@@ -131,6 +193,115 @@ module Mongo
|
|
131
193
|
command.merge!(lsid: session_id)
|
132
194
|
end
|
133
195
|
|
196
|
+
# Add the startTransaction field to a command document if applicable.
|
197
|
+
#
|
198
|
+
# @example
|
199
|
+
# session.add_start_transaction!(cmd)
|
200
|
+
#
|
201
|
+
# @return [ Hash, BSON::Document ] The command document.
|
202
|
+
#
|
203
|
+
# @since 2.6.0
|
204
|
+
def add_start_transaction!(command)
|
205
|
+
command.tap do |c|
|
206
|
+
c[:startTransaction] = true if starting_transaction?
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Add the transaction number to a command document if applicable.
|
211
|
+
#
|
212
|
+
# @example
|
213
|
+
# session.add_txn_num!(cmd)
|
214
|
+
#
|
215
|
+
# @return [ Hash, BSON::Document ] The command document.
|
216
|
+
#
|
217
|
+
# @since 2.6.0
|
218
|
+
def add_txn_num!(command)
|
219
|
+
command.tap do |c|
|
220
|
+
c[:txnNumber] = BSON::Int64.new(@server_session.txn_num) if in_transaction?
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Add the transactions options if applicable.
|
225
|
+
#
|
226
|
+
# @example
|
227
|
+
# session.add_txn_opts!(cmd)
|
228
|
+
#
|
229
|
+
# @return [ Hash, BSON::Document ] The command document.
|
230
|
+
#
|
231
|
+
# @since 2.6.0
|
232
|
+
def add_txn_opts!(command, read)
|
233
|
+
command.tap do |c|
|
234
|
+
# The read preference should be added for all read operations.
|
235
|
+
c['$readPreference'] = txn_read_pref if read && txn_read_pref
|
236
|
+
|
237
|
+
# The read concern should be added to any command that starts a transaction.
|
238
|
+
if starting_transaction? && txn_read_concern
|
239
|
+
c[:readConcern] ||= {}
|
240
|
+
c[:readConcern].merge!(txn_read_concern)
|
241
|
+
end
|
242
|
+
|
243
|
+
# We need to send the read concern level as a string rather than a symbol.
|
244
|
+
if c[:readConcern] && c[:readConcern][:level]
|
245
|
+
c[:readConcern][:level] = c[:readConcern][:level].to_s
|
246
|
+
end
|
247
|
+
|
248
|
+
# The write concern should be added to any abortTransaction or commitTransaction command.
|
249
|
+
if (c[:abortTransaction] || c[:commitTransaction]) && txn_write_concern
|
250
|
+
c[:writeConcern] = txn_write_concern
|
251
|
+
end
|
252
|
+
|
253
|
+
# A non-numeric write concern w value needs to be sent as a string rather than a symbol.
|
254
|
+
if c[:writeConcern] && c[:writeConcern][:w] && c[:writeConcern][:w].is_a?(Symbol)
|
255
|
+
c[:writeConcern][:w] = c[:writeConcern][:w].to_s
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Remove the read concern and/or write concern from the command if not applicable.
|
261
|
+
#
|
262
|
+
# @example
|
263
|
+
# session.suppress_read_write_concern!(cmd)
|
264
|
+
#
|
265
|
+
# @return [ Hash, BSON::Document ] The command document.
|
266
|
+
#
|
267
|
+
# @since 2.6.0
|
268
|
+
def suppress_read_write_concern!(command)
|
269
|
+
command.tap do |c|
|
270
|
+
next unless in_transaction?
|
271
|
+
|
272
|
+
c.delete(:readConcern) unless starting_transaction?
|
273
|
+
c.delete(:writeConcern) unless c[:commitTransaction] || c[:abortTransaction]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Ensure that the read preference of a command primary.
|
278
|
+
#
|
279
|
+
# @example
|
280
|
+
# session.validate_read_pref!(command)
|
281
|
+
#
|
282
|
+
# @raise [ Mongo::Error::InvalidTransactionOperation ] If the read preference of the command is
|
283
|
+
# not primary.
|
284
|
+
#
|
285
|
+
# @since 2.6.0
|
286
|
+
def validate_read_pref!(command)
|
287
|
+
return unless in_transaction? && non_primary_readpref?(command)
|
288
|
+
|
289
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
290
|
+
Mongo::Error::InvalidTransactionOperation::INVALID_READ_PREFERENCE)
|
291
|
+
end
|
292
|
+
|
293
|
+
# Update the state of the session due to a (non-commit and non-abort) operation being run.
|
294
|
+
#
|
295
|
+
# @since 2.6.0
|
296
|
+
def update_state!
|
297
|
+
case @state
|
298
|
+
when STARTING_TRANSACTION_STATE
|
299
|
+
@state = TRANSACTION_IN_PROGRESS_STATE
|
300
|
+
when TRANSACTION_COMMITTED_STATE, TRANSACTION_ABORTED_STATE
|
301
|
+
@state = NO_TRANSACTION_STATE
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
134
305
|
# Validate the session.
|
135
306
|
#
|
136
307
|
# @example
|
@@ -203,7 +374,7 @@ module Mongo
|
|
203
374
|
@operation_time = new_operation_time
|
204
375
|
end
|
205
376
|
end
|
206
|
-
|
377
|
+
|
207
378
|
# Will writes executed with this session be retried.
|
208
379
|
#
|
209
380
|
# @example Will writes be retried.
|
@@ -211,8 +382,8 @@ module Mongo
|
|
211
382
|
#
|
212
383
|
# @return [ true, false ] If writes will be retried.
|
213
384
|
#
|
214
|
-
# @note Retryable writes are only available on server versions at least 3.6
|
215
|
-
# sharded clusters or replica sets.
|
385
|
+
# @note Retryable writes are only available on server versions at least 3.6
|
386
|
+
# and with sharded clusters or replica sets.
|
216
387
|
#
|
217
388
|
# @since 2.5.0
|
218
389
|
def retry_writes?
|
@@ -243,6 +414,18 @@ module Mongo
|
|
243
414
|
@server_session.next_txn_num if @server_session
|
244
415
|
end
|
245
416
|
|
417
|
+
# Get the current transaction number.
|
418
|
+
#
|
419
|
+
# @example Get the current transaction number.
|
420
|
+
# session.txn_num
|
421
|
+
#
|
422
|
+
# @return [ Integer ] The current transaction number.
|
423
|
+
#
|
424
|
+
# @since 2.6.0
|
425
|
+
def txn_num
|
426
|
+
@server_session && @server_session.txn_num
|
427
|
+
end
|
428
|
+
|
246
429
|
# Is this session an implicit one (not user-created).
|
247
430
|
#
|
248
431
|
# @example Is the session implicit?
|
@@ -267,8 +450,202 @@ module Mongo
|
|
267
450
|
@explicit ||= !implicit?
|
268
451
|
end
|
269
452
|
|
453
|
+
# Start a new transaction.
|
454
|
+
#
|
455
|
+
# Note that the transaction will not be started on the server until an operation is performed
|
456
|
+
# after start_transaction is called.
|
457
|
+
#
|
458
|
+
# @example Start a new transaction
|
459
|
+
# session.start_transaction(options)
|
460
|
+
#
|
461
|
+
# @raise [ InvalidTransactionOperation ] If a transaction is already in progress or if the
|
462
|
+
# write concern is unacknowledged.
|
463
|
+
#
|
464
|
+
# @since 2.6.0
|
465
|
+
def start_transaction(options = nil)
|
466
|
+
check_if_ended!
|
467
|
+
|
468
|
+
if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
469
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
470
|
+
Mongo::Error::InvalidTransactionOperation::TRANSACTION_ALREADY_IN_PROGRESS)
|
471
|
+
end
|
472
|
+
|
473
|
+
next_txn_num
|
474
|
+
@txn_options = options || @options[:default_transaction_options] || {}
|
475
|
+
|
476
|
+
if txn_write_concern && WriteConcern.send(:unacknowledged?, txn_write_concern)
|
477
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
478
|
+
Mongo::Error::InvalidTransactionOperation::UNACKNOWLEDGED_WRITE_CONCERN)
|
479
|
+
end
|
480
|
+
|
481
|
+
@state = STARTING_TRANSACTION_STATE
|
482
|
+
end
|
483
|
+
|
484
|
+
# Commit the currently active transaction on the session.
|
485
|
+
#
|
486
|
+
# @example Commits the transaction.
|
487
|
+
# session.commit_transaction
|
488
|
+
#
|
489
|
+
# @raise [ InvalidTransactionOperation ] If a transaction was just aborted and no new one was
|
490
|
+
# started.
|
491
|
+
#
|
492
|
+
# @since 2.6.0
|
493
|
+
def commit_transaction
|
494
|
+
check_if_ended!
|
495
|
+
check_if_no_transaction!
|
496
|
+
|
497
|
+
if within_states?(TRANSACTION_ABORTED_STATE)
|
498
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
499
|
+
Mongo::Error::InvalidTransactionOperation.cannot_call_after_msg(
|
500
|
+
:abortTransaction, :commitTransaction))
|
501
|
+
end
|
502
|
+
|
503
|
+
begin
|
504
|
+
# If commitTransaction is called twice, we need to run the same commit operation again, so
|
505
|
+
# we revert the session to the previous state.
|
506
|
+
if within_states?(TRANSACTION_COMMITTED_STATE)
|
507
|
+
@state = @last_commit_skipped ? STARTING_TRANSACTION_STATE : TRANSACTION_IN_PROGRESS_STATE
|
508
|
+
end
|
509
|
+
|
510
|
+
if starting_transaction?
|
511
|
+
@last_commit_skipped = true
|
512
|
+
else
|
513
|
+
@last_commit_skipped = false
|
514
|
+
|
515
|
+
write_with_retry(self, txn_options[:write_concern], true) do |server, txn_num|
|
516
|
+
Operation::Command.new(
|
517
|
+
selector: { commitTransaction: 1 },
|
518
|
+
db_name: 'admin',
|
519
|
+
session: self,
|
520
|
+
txn_num: txn_num
|
521
|
+
).execute(server)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
rescue Mongo::Error::NoServerAvailable, Mongo::Error::SocketError => e
|
525
|
+
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
526
|
+
raise e
|
527
|
+
rescue Mongo::Error::OperationFailure => e
|
528
|
+
err_doc = e.instance_variable_get(:@result).send(:first_document)
|
529
|
+
|
530
|
+
if e.write_retryable? || (err_doc['writeConcernError'] &&
|
531
|
+
!UNLABELED_WRITE_CONCERN_CODES.include?(err_doc['writeConcernError']['code']))
|
532
|
+
e.send(:add_label, Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)
|
533
|
+
end
|
534
|
+
|
535
|
+
raise e
|
536
|
+
ensure
|
537
|
+
@state = TRANSACTION_COMMITTED_STATE
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
# Abort the currently active transaction without making any changes to the database.
|
542
|
+
#
|
543
|
+
# @example Abort the transaction.
|
544
|
+
# session.abort_transaction
|
545
|
+
#
|
546
|
+
# @raise [ Mongo::Error::InvalidTransactionOperation ] If a transaction was just committed or
|
547
|
+
# aborted and no new one was started.
|
548
|
+
#
|
549
|
+
# @since 2.6.0
|
550
|
+
def abort_transaction
|
551
|
+
check_if_ended!
|
552
|
+
check_if_no_transaction!
|
553
|
+
|
554
|
+
if within_states?(TRANSACTION_COMMITTED_STATE)
|
555
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
556
|
+
Mongo::Error::InvalidTransactionOperation.cannot_call_after_msg(
|
557
|
+
:commitTransaction, :abortTransaction))
|
558
|
+
end
|
559
|
+
|
560
|
+
if within_states?(TRANSACTION_ABORTED_STATE)
|
561
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
562
|
+
Mongo::Error::InvalidTransactionOperation.cannot_call_twice_msg(:abortTransaction))
|
563
|
+
end
|
564
|
+
|
565
|
+
begin
|
566
|
+
unless starting_transaction?
|
567
|
+
write_with_retry(self, txn_options[:write_concern], true) do |server, txn_num|
|
568
|
+
Operation::Command.new(
|
569
|
+
selector: { abortTransaction: 1 },
|
570
|
+
db_name: 'admin',
|
571
|
+
session: self,
|
572
|
+
txn_num: txn_num
|
573
|
+
).execute(server)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
@state = TRANSACTION_ABORTED_STATE
|
578
|
+
rescue Mongo::Error::InvalidTransactionOperation
|
579
|
+
raise
|
580
|
+
rescue Mongo::Error
|
581
|
+
@state = TRANSACTION_ABORTED_STATE
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
# Whether or not the session is currently in a transaction.
|
586
|
+
#
|
587
|
+
# @example Is the session in a transaction?
|
588
|
+
# session.in_transaction?
|
589
|
+
#
|
590
|
+
# @return [ true | false ] Whether or not the session in a transaction.
|
591
|
+
#
|
592
|
+
# @since 2.6.0
|
593
|
+
def in_transaction?
|
594
|
+
within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
|
595
|
+
end
|
596
|
+
|
597
|
+
# Get the read preference document the session will use in the currently active transaction.
|
598
|
+
#
|
599
|
+
# @example Get the transaction's read preference
|
600
|
+
# session.txn_read_pref
|
601
|
+
#
|
602
|
+
# @return [ Hash ] The read preference document of the transaction.
|
603
|
+
#
|
604
|
+
# @since 2.6.0
|
605
|
+
def txn_read_pref
|
606
|
+
rp = (txn_options && txn_options[:read_preference] && txn_options[:read_preference].dup) ||
|
607
|
+
(@client.read_preference && @client.read_preference.dup)
|
608
|
+
rp[:mode] = rp[:mode].to_s if rp
|
609
|
+
rp
|
610
|
+
end
|
611
|
+
|
612
|
+
def cluster
|
613
|
+
@client.cluster
|
614
|
+
end
|
615
|
+
|
270
616
|
private
|
271
617
|
|
618
|
+
def within_states?(*states)
|
619
|
+
states.include?(@state)
|
620
|
+
end
|
621
|
+
|
622
|
+
def starting_transaction?
|
623
|
+
within_states?(STARTING_TRANSACTION_STATE)
|
624
|
+
end
|
625
|
+
|
626
|
+
def check_if_no_transaction!
|
627
|
+
return unless within_states?(NO_TRANSACTION_STATE)
|
628
|
+
|
629
|
+
raise Mongo::Error::InvalidTransactionOperation.new(
|
630
|
+
Mongo::Error::InvalidTransactionOperation::NO_TRANSACTION_STARTED)
|
631
|
+
end
|
632
|
+
|
633
|
+
def txn_read_concern
|
634
|
+
txn_options && txn_options[:read_concern] || @client.read_concern
|
635
|
+
end
|
636
|
+
|
637
|
+
def txn_write_concern
|
638
|
+
(txn_options && txn_options[:write_concern]) ||
|
639
|
+
(@client.write_concern && @client.write_concern.options)
|
640
|
+
end
|
641
|
+
|
642
|
+
def non_primary_readpref?(command)
|
643
|
+
return false unless command['$readPreference']
|
644
|
+
|
645
|
+
mode = command['$readPreference']['mode'] || command['$readPreference'][:mode]
|
646
|
+
mode && mode != 'primary'
|
647
|
+
end
|
648
|
+
|
272
649
|
def causal_consistency_doc(read_concern)
|
273
650
|
if operation_time && causal_consistency?
|
274
651
|
(read_concern || {}).merge(:afterClusterTime => operation_time)
|
@@ -306,7 +683,7 @@ module Mongo
|
|
306
683
|
end
|
307
684
|
|
308
685
|
def check_matching_cluster!(cluster)
|
309
|
-
if @cluster != cluster
|
686
|
+
if @client.cluster != cluster
|
310
687
|
raise Mongo::Error::InvalidSession.new(MISMATCHED_CLUSTER_ERROR_MSG)
|
311
688
|
end
|
312
689
|
end
|