Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@ explicitly in your Gemfile:
gem 'redis-objects', '>= 2.0.0.beta'
~~~
You're encouraged to try it out in test code (not production) to ensure it works for you.
Official release is expected later in 2023.
Official release is expected eventually.

Key Naming Changes
------------------
The internal key naming scheme has changed for `Nested::Class::Namespaces` to fix a longstanding bug.
**This means your existing data in Redis will not be accessible until you call `migrate_redis_legacy_keys`.**
A new method to generate the internal key naming scheme has been added to fix a
longstanding bug. (Refer to [#213](https://github.com/nateware/redis-objects/issues/231))
It can be controlled by `Redis::Objects.prefix_style =` either `:legacy` (default)
or `:modern`.
If your Redis::Object enhanced classes are nested such as `Nested::Class::Namespaces`
then you should upgrade them using the below proceedure. (only needed once)

To fix this (only needed once), create a script like this:
Create a script like this:

~~~ruby
class YouClassNameHere < ActiveRecord::Base
include Redis::Objects
# ... your relevant definitions here ...
# ... your relevant redis_object definitions here (counters/sets) ...
end

YourClassName.migrate_redis_legacy_keys
Expand All @@ -37,7 +41,10 @@ Then, you need to find a time when you can temporarily pause writes to your redi
so that you can run that script. It uses `redis.scan` internally so it should be able to
handle a high number of keys. For large data sets, it could take a while.

For more details on the issue and fix refer to [#213](https://github.com/nateware/redis-objects/issues/231).
After migrating all of your redis keys, update `Redis::Objects.prefix_style = :modern` to
start using the new keys.
**Your existing data in Redis will not be accessible after running `migrate_redis_legacy_keys`
until the prefix_style has been changed.**

Renaming of `lock` Method
-------------------------
Expand Down
57 changes: 40 additions & 17 deletions lib/redis/objects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ def redis
raise(NotConnected, "Redis::Objects.redis not set to a Redis.new connection")
end

# Toggles whether to use the legacy redis key naming scheme, which causes
# naming conflicts in certain cases.
# (attr_accessor with a default value)
attr_writer :prefix_style
def prefix_style
# NOTE: In a future release the default will change to :modern
@prefix_style ||= :legacy
end

def redis_legacy_naming?
prefix_style == :legacy
end

def included(klass)
# Core (this file)
klass.instance_variable_set(:@redis, nil)
Expand Down Expand Up @@ -101,11 +114,6 @@ def redis_objects
@redis_objects ||= {}
end

# Toggles whether to use the legacy redis key naming scheme, which causes
# naming conflicts in certain cases.
attr_accessor :redis_legacy_naming
attr_accessor :redis_silence_warnings

# Set the Redis redis_prefix to use. Defaults to class_name.
def redis_prefix=(redis_prefix)
@silence_warnings_as_redis_prefix_was_set_manually = true
Expand All @@ -114,10 +122,10 @@ def redis_prefix=(redis_prefix)

def redis_prefix(klass = self) #:nodoc:
@redis_prefix ||=
if redis_legacy_naming
if Objects.redis_legacy_naming?
redis_legacy_naming_warning_message(klass)
redis_legacy_prefix(klass)
else
redis_legacy_naming_warning_message(klass)
redis_modern_prefix(klass)
end

Expand All @@ -140,33 +148,44 @@ def redis_legacy_prefix(klass = self) #:nodoc:
downcase
end

# Temporary warning to help with migrating key names
attr_accessor :redis_silence_warnings

# Temporary warning to help with migrating key names
def redis_legacy_naming_warning_message(klass)
# warn @silence_warnings_as_redis_prefix_was_set_manually.inspect
unless redis_legacy_naming || redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually
modern = redis_modern_prefix(klass)
legacy = redis_legacy_prefix(klass)
if modern != legacy
warn <<EOW
return if redis_silence_warnings || @silence_warnings_as_redis_prefix_was_set_manually

modern = redis_modern_prefix(klass)
legacy = redis_legacy_prefix(klass)
return if modern == legacy

warn <<EOW
[redis-objects] WARNING: In redis-objects 2.0.0, key naming will change to fix longstanding bugs.
[redis-objects] Your class #{klass.name.to_s} will be affected by this change!
[redis-objects] Current key prefix: #{legacy.inspect}
[redis-objects] Future key prefix: #{modern.inspect}
[redis-objects] Read more at https://github.com/nateware/redis-objects/issues/231
EOW
end
end
end

def migrate_redis_legacy_keys
cursor = 0
unless Objects.redis_legacy_naming?
raise "Redis::Objects is already configured to use modern key prefixes."
end

legacy = redis_legacy_prefix
total_keys = 0
if legacy == redis_prefix
raise "Failed to migrate keys for #{self.name.to_s} as legacy and new redis_prefix are the same (#{redis_prefix})"
end
warn "[redis-objects] Migrating keys from #{legacy} prefix to #{redis_prefix}"

cursor = 0
total_keys = 0

# Temporarily update the prefix to modern while we update these keys.
# NOTE: we cannot simply adjust the prefix_style, because the prefix is cached by @redis_prefix
self.redis_prefix = modern

loop do
cursor, keys = redis.scan(cursor, :match => "#{legacy}:*")
total_keys += keys.length
Expand All @@ -183,6 +202,10 @@ def migrate_redis_legacy_keys
warn "[redis-objects] Warning: Rename '#{key}', '#{new_key}' failed: #{ok}" if ok != 'OK'
end
break if cursor == "0"

ensure
# Change the prefix back (just in case)
self.redis_prefix = legacy
end

warn "[redis-objects] Migrated #{total_keys} total number of redis keys"
Expand Down
Loading