module CouchbaseId::Generator
- NOTE
-
incr, decr, append, prepend == atomic
Public Class Methods
default_class_id_generator(overflow, count)
click to toggle source
# File lib/couchbase-id/generator.rb, line 96 def self.default_class_id_generator(overflow, count) id = Radix.convert(overflow, B10, B64) + Radix.convert(count, B10, B64) "#{self.design_document}_#{CLUSTER_ID}-#{id}" end
included(base)
click to toggle source
# File lib/couchbase-id/generator.rb, line 80 def self.included(base) class << base attr_accessor :__overflow__ attr_accessor :__class_id_generator__ end base.class_eval do # # Best case we have 18446744073709551615 * 18446744073709551615 model entries for each database cluster # and we can always change the cluster id if this limit is reached # define_model_callbacks :save, :create before_save :generate_id before_create :generate_id def self.default_class_id_generator(overflow, count) id = Radix.convert(overflow, B10, B64) + Radix.convert(count, B10, B64) "#{self.design_document}_#{CLUSTER_ID}-#{id}" end # # Override the default hashing function # def self.set_class_id_generator(callback = nil, &block) callback ||= block self.__class_id_generator__ = callback end # # Configure class level variables base.__overflow__ = nil base.__class_id_generator__ = method(:default_class_id_generator) end end
set_class_id_generator(callback = nil, &block)
click to toggle source
Override the default hashing function
# File lib/couchbase-id/generator.rb, line 104 def self.set_class_id_generator(callback = nil, &block) callback ||= block self.__class_id_generator__ = callback end
Public Instance Methods
generate_id()
click to toggle source
instance method
# File lib/couchbase-id/generator.rb, line 36 def generate_id if self.id.nil? # # Generate the id (incrementing values as required) # overflow = self.class.__overflow__ ||= self.class.bucket.get("#{self.class.design_document}:#{CLUSTER_ID}:overflow", :quiet => true) # Don't error if not there count = self.class.bucket.incr("#{self.class.design_document}:#{CLUSTER_ID}:count", :create => true) # This models current id count if count == 0 || overflow.nil? overflow ||= 0 overflow += 1 # We shouldn't need to worry about concurrency here due to the size of count # Would require ~18446744073709551615 concurrent writes self.class.bucket.set("#{self.class.design_document}:#{CLUSTER_ID}:overflow", overflow) self.class.__overflow__ = overflow end self.id = self.class.__class_id_generator__.call(overflow, count) # # So an existing id would only be present if: # => something crashed before incrementing the overflow # => this is another request was occurring before the overflow is incremented # # Basically only the overflow should be able to cause issues, we'll increment the count just to be sure # One would hope this code only ever runs under high load during an overflow event # while self.class.bucket.get(self.id, :quiet => true).present? # Set in-case we are here due to a crash (concurrency is not an issue) # Note we are not incrementing the @__overflow__ variable self.class.bucket.set("#{self.class.design_document}:#{CLUSTER_ID}:overflow", overflow + 1) count = self.class.bucket.incr("#{self.class.design_document}:#{CLUSTER_ID}:count") # Increment just in case (attempt to avoid infinite loops) # Reset the overflow if self.class.__overflow__ == overflow self.class.__overflow__ = nil end # Generate the new id self.id = self.class.__class_id_generator__.call(overflow + 1, count) end end end