Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mongo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
require 'mongo/csot_timeout_holder'
require 'mongo/options'
require 'mongo/loggable'
require 'mongo/deprecations'
require 'mongo/cluster_time'
require 'mongo/topology_version'
require 'mongo/monitoring'
Expand Down
99 changes: 99 additions & 0 deletions lib/mongo/deprecations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true

require 'mongo/loggable'

module Mongo
# Used for reporting deprecated behavior in the driver. When it is possible
# to detect that a deprecated feature is being used, a warning should be issued
# through this module.
#
# The warning will be issued no more than once for that feature, regardless
# of how many times Mongo::Deprecations.warn is called.
#
# @example Issue a deprecation warning.
# Mongo::Deprecations.warn(:old_feature, "The old_feature is deprecated, use new_feature instead.")
#
# @api private
module Deprecations
extend self

include Mongo::Loggable

# Mutex for synchronizing access to warned features.
# @api private
MUTEX = Thread::Mutex.new

# Issue a warning about a deprecated feature. The warning is written to the
# logger, and will not be written more than once per feature.
Comment thread
jamis marked this conversation as resolved.
#
# @param [ String | Symbol ] feature The deprecated feature.
# @param [ String ] message The deprecation message.
def warn(feature, message)
MUTEX.synchronize do
return if _warned?(feature)

_warned!(feature)
log_warn("[DEPRECATION:#{feature}] #{message}")
end
end

# Check if a warning for a given deprecated feature has already been issued.
#
# @param [ String | Symbol ] feature The deprecated feature.
# @param [ true | false ] prefix Whether to check for prefix matches.
#
# @return [ true | false ] If a warning has already been issued.
def warned?(feature, prefix: false)
MUTEX.synchronize { _warned?(feature, prefix: prefix) }
end

# Mark that a warning for a given deprecated feature has been issued.
#
# @param [ String | Symbol ] feature The deprecated feature.
Comment thread
jamis marked this conversation as resolved.
def warned!(feature)
MUTEX.synchronize { _warned!(feature) }
nil
end

# Clears all memory of previously warned features.
def clear!
MUTEX.synchronize { warned_features reset: true }
nil
end
Comment thread
jamis marked this conversation as resolved.

private

# Set of features that have already been warned about.
#
# @param [ true | false ] reset Whether to reset the warned features.
#
# @return [ Set<String> ] The set of warned features.
def warned_features(reset: false)
@warned_features = nil if reset
@warned_features ||= Set.new
end

# Check if a warning for a given deprecated feature has already been issued.
# This version is not thread-safe.
#
# @param [ String | Symbol ] feature The deprecated feature.
# @param [ true | false ] prefix Whether to check for prefix matches.
#
# @return [ true | false ] If a warning has already been issued.
def _warned?(feature, prefix: false)
if prefix
warned_features.any? { |f| feature.to_s.start_with?(f) }
else
warned_features.include?(feature.to_s)
end
end

# Mark that a warning for a given deprecated feature has been issued.
# This version is not thread-safe.
#
# @param [ String | Symbol ] feature The deprecated feature.
def _warned!(feature)
warned_features.add(feature.to_s)
end
end
end
45 changes: 37 additions & 8 deletions lib/mongo/server/description/features.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,44 @@ class Features
SERVER_TOO_OLD = "Server at (%s) reports wire version (%s), but this version of the Ruby driver " +
"requires at least (%s)."

# Warning message if the server version is deprecated.
SERVER_DEPRECATED = 'Server at (%s) reports wire version (%s), but support for that wire version ' \
'is deprecated and will be removed in a future version of the Ruby driver. ' \
'Please upgrade your MongoDB server to a newer version soon.'

# Error message if the driver is too old for the version of the server.
#
# @since 2.5.0
DRIVER_TOO_OLD = "Server at (%s) requires wire version (%s), but this version of the Ruby driver " +
"only supports up to (%s)."

# An empty range constant, for use in DEPRECATED_WIRE_VERSIONS.
EMPTY_RANGE = (0...0).freeze

# The wire protocol versions that this version of the driver supports.
#
# @since 2.0.0
DRIVER_WIRE_VERSIONS = (6..25).freeze
DRIVER_WIRE_VERSIONS = 6..25

# The wire protocol versions that are deprecated in this version of the
# driver. Support for these versions will be removed in the future.
#
# If there are multiple currently-deprecated wire versions, this should
# be set to a range of those versions.
#
# If there is only a single currently-deprecated wire version, this should
# be set to a range where the min and max are the same value.
#
# If there are no currently-deprecated wire versions, this should be
# set to an empty range (e.g. the EMPTY_RANGE constant).
DEPRECATED_WIRE_VERSIONS = 6..6

Comment thread
jamis marked this conversation as resolved.
# make sure the deprecated versions are valid
if DEPRECATED_WIRE_VERSIONS.min
if DRIVER_WIRE_VERSIONS.min > DEPRECATED_WIRE_VERSIONS.max
raise ArgumentError, 'DEPRECATED_WIRE_VERSIONS must be empty, or be within DRIVER_WIRE_VERSIONS'
end
end

# Create the methods for each mapping to tell if they are supported.
#
Expand Down Expand Up @@ -131,20 +159,21 @@ def initialize(server_wire_versions, address = nil)
end

# Check that there is an overlap between the driver supported wire
# version range and the server wire version range.
#
# @example Verify the wire version overlap.
# features.check_driver_support!
# version range and the server wire version range. Also checks to see
# if the server is using a deprecated wire version.
#
# @raise [ Error::UnsupportedFeatures ] If the wire version range is
# not covered by the driver.
#
# @since 2.5.1
def check_driver_support!
if DRIVER_WIRE_VERSIONS.min > @server_wire_versions.max
if DEPRECATED_WIRE_VERSIONS.include?(@server_wire_versions.max)
feature = "wire_version:#{@address}"
Mongo::Deprecations.warn(feature, SERVER_DEPRECATED % [@address, @server_wire_versions.max])

elsif DRIVER_WIRE_VERSIONS.min > @server_wire_versions.max
raise Error::UnsupportedFeatures.new(SERVER_TOO_OLD % [@address,
@server_wire_versions.max,
DRIVER_WIRE_VERSIONS.min])

elsif DRIVER_WIRE_VERSIONS.max < @server_wire_versions.min
raise Error::UnsupportedFeatures.new(DRIVER_TOO_OLD % [@address,
@server_wire_versions.min,
Expand Down
20 changes: 20 additions & 0 deletions spec/mongo/server/description/features_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@
end
end

if described_class::DEPRECATED_WIRE_VERSIONS.any?
context 'when the max server wire version range is deprecated' do
before do
Mongo::Deprecations.clear!
end

let(:wire_versions) do
(described_class::DEPRECATED_WIRE_VERSIONS.min - 1)..described_class::DEPRECATED_WIRE_VERSIONS.max
end

it 'issues a deprecation warning' do
expect {
features.check_driver_support!
}.to change {
Mongo::Deprecations.warned?("wire_version:#{default_address}")
}.from(false).to(true)
end
end
end

context 'when the server wire version range max is higher' do

let(:wire_versions) do
Expand Down
Loading