Class: InternalId::ImplicitlyLockingInternalIdGenerator
- Inherits:
-
Object
- Object
- InternalId::ImplicitlyLockingInternalIdGenerator
- Defined in:
- app/models/internal_id.rb
Constant Summary collapse
- RecordAlreadyExists =
Class.new(StandardError)
Instance Attribute Summary collapse
-
#init ⇒ Object
readonly
Generate next internal id for a given scope and usage.
-
#scope ⇒ Object
readonly
Generate next internal id for a given scope and usage.
-
#scope_attrs ⇒ Object
readonly
Generate next internal id for a given scope and usage.
-
#subject ⇒ Object
readonly
Generate next internal id for a given scope and usage.
-
#usage ⇒ Object
readonly
Generate next internal id for a given scope and usage.
Instance Method Summary collapse
-
#generate ⇒ Object
Generates next internal id and returns it init: Block that gets called to initialize InternalId record if not present Make sure to not throw exceptions in the absence of records (if this is expected).
-
#initialize(subject, scope, usage, init = nil) ⇒ ImplicitlyLockingInternalIdGenerator
constructor
A new instance of ImplicitlyLockingInternalIdGenerator.
-
#reset(value) ⇒ Object
Reset tries to rewind to ‘value-1`.
-
#track_greatest(new_value) ⇒ Object
Create a record in internal_ids if one does not yet exist and set its new_value if it is higher than the current last_value.
Constructor Details
#initialize(subject, scope, usage, init = nil) ⇒ ImplicitlyLockingInternalIdGenerator
Returns a new instance of ImplicitlyLockingInternalIdGenerator.
101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'app/models/internal_id.rb', line 101 def initialize(subject, scope, usage, init = nil) @subject = subject @scope = scope @usage = usage @init = init raise ArgumentError, 'Scope is not well-defined, need at least one column for scope (given: 0)' if scope.empty? unless InternalId.usages.has_key?(usage.to_s) raise ArgumentError, "Usage '#{usage}' is unknown. Supported values are #{InternalId.usages.keys} from InternalId.usages" end end |
Instance Attribute Details
#init ⇒ Object (readonly)
Generate next internal id for a given scope and usage.
For currently supported usages, see #usage enum.
The method implements a locking scheme that has the following properties: 1) Generated sequence of internal ids is unique per (scope and usage) 2) The method is thread-safe and may be used in concurrent threads/processes. 3) The generated sequence is gapless. 4) In the absence of a record in the internal_ids table, one will be created
and last_value will be calculated on the fly.
subject: The instance or class we’re generating an internal id for. scope: Attributes that define the scope for id generation.
Valid keys are `project/project_id` and `namespace/namespace_id`.
usage: Symbol to define the usage of the internal id, see InternalId.usages init: Proc that accepts the subject and the scope and returns Integer|NilClass
97 98 99 |
# File 'app/models/internal_id.rb', line 97 def init @init end |
#scope ⇒ Object (readonly)
Generate next internal id for a given scope and usage.
For currently supported usages, see #usage enum.
The method implements a locking scheme that has the following properties: 1) Generated sequence of internal ids is unique per (scope and usage) 2) The method is thread-safe and may be used in concurrent threads/processes. 3) The generated sequence is gapless. 4) In the absence of a record in the internal_ids table, one will be created
and last_value will be calculated on the fly.
subject: The instance or class we’re generating an internal id for. scope: Attributes that define the scope for id generation.
Valid keys are `project/project_id` and `namespace/namespace_id`.
usage: Symbol to define the usage of the internal id, see InternalId.usages init: Proc that accepts the subject and the scope and returns Integer|NilClass
97 98 99 |
# File 'app/models/internal_id.rb', line 97 def scope @scope end |
#scope_attrs ⇒ Object (readonly)
Generate next internal id for a given scope and usage.
For currently supported usages, see #usage enum.
The method implements a locking scheme that has the following properties: 1) Generated sequence of internal ids is unique per (scope and usage) 2) The method is thread-safe and may be used in concurrent threads/processes. 3) The generated sequence is gapless. 4) In the absence of a record in the internal_ids table, one will be created
and last_value will be calculated on the fly.
subject: The instance or class we’re generating an internal id for. scope: Attributes that define the scope for id generation.
Valid keys are `project/project_id` and `namespace/namespace_id`.
usage: Symbol to define the usage of the internal id, see InternalId.usages init: Proc that accepts the subject and the scope and returns Integer|NilClass
97 98 99 |
# File 'app/models/internal_id.rb', line 97 def scope_attrs @scope_attrs end |
#subject ⇒ Object (readonly)
Generate next internal id for a given scope and usage.
For currently supported usages, see #usage enum.
The method implements a locking scheme that has the following properties: 1) Generated sequence of internal ids is unique per (scope and usage) 2) The method is thread-safe and may be used in concurrent threads/processes. 3) The generated sequence is gapless. 4) In the absence of a record in the internal_ids table, one will be created
and last_value will be calculated on the fly.
subject: The instance or class we’re generating an internal id for. scope: Attributes that define the scope for id generation.
Valid keys are `project/project_id` and `namespace/namespace_id`.
usage: Symbol to define the usage of the internal id, see InternalId.usages init: Proc that accepts the subject and the scope and returns Integer|NilClass
97 98 99 |
# File 'app/models/internal_id.rb', line 97 def subject @subject end |
#usage ⇒ Object (readonly)
Generate next internal id for a given scope and usage.
For currently supported usages, see #usage enum.
The method implements a locking scheme that has the following properties: 1) Generated sequence of internal ids is unique per (scope and usage) 2) The method is thread-safe and may be used in concurrent threads/processes. 3) The generated sequence is gapless. 4) In the absence of a record in the internal_ids table, one will be created
and last_value will be calculated on the fly.
subject: The instance or class we’re generating an internal id for. scope: Attributes that define the scope for id generation.
Valid keys are `project/project_id` and `namespace/namespace_id`.
usage: Symbol to define the usage of the internal id, see InternalId.usages init: Proc that accepts the subject and the scope and returns Integer|NilClass
97 98 99 |
# File 'app/models/internal_id.rb', line 97 def usage @usage end |
Instance Method Details
#generate ⇒ Object
Generates next internal id and returns it init: Block that gets called to initialize InternalId record if not present
Make sure to not throw exceptions in the absence of records (if this is expected).
117 118 119 120 121 122 123 124 125 126 127 |
# File 'app/models/internal_id.rb', line 117 def generate InternalId.internal_id_transactions_increment(operation: :generate, usage: usage) next_iid = update_record!(subject, scope, usage, arel_table[:last_value] + 1) return next_iid if next_iid create_record!(subject, scope, usage, initial_value(subject, scope) + 1) rescue RecordAlreadyExists retry end |
#reset(value) ⇒ Object
Reset tries to rewind to ‘value-1`. This will only succeed, if `value` stored in database is equal to `last_value`. value: The expected last_value to decrement
132 133 134 135 136 137 138 139 |
# File 'app/models/internal_id.rb', line 132 def reset(value) return false unless value InternalId.internal_id_transactions_increment(operation: :reset, usage: usage) iid = update_record!(subject, scope.merge(last_value: value), usage, arel_table[:last_value] - 1) iid == value - 1 end |
#track_greatest(new_value) ⇒ Object
Create a record in internal_ids if one does not yet exist and set its new_value if it is higher than the current last_value
143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'app/models/internal_id.rb', line 143 def track_greatest(new_value) InternalId.internal_id_transactions_increment(operation: :track_greatest, usage: usage) function = Arel::Nodes::NamedFunction.new('GREATEST', [arel_table[:last_value], new_value.to_i]) next_iid = update_record!(subject, scope, usage, function) return next_iid if next_iid create_record!(subject, scope, usage, [initial_value(subject, scope), new_value].max) rescue RecordAlreadyExists retry end |