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.
102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'app/models/internal_id.rb', line 102 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
98 99 100 |
# File 'app/models/internal_id.rb', line 98 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
98 99 100 |
# File 'app/models/internal_id.rb', line 98 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
98 99 100 |
# File 'app/models/internal_id.rb', line 98 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
98 99 100 |
# File 'app/models/internal_id.rb', line 98 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
98 99 100 |
# File 'app/models/internal_id.rb', line 98 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).
118 119 120 121 122 123 124 125 126 127 128 |
# File 'app/models/internal_id.rb', line 118 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
133 134 135 136 137 138 139 140 |
# File 'app/models/internal_id.rb', line 133 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
144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'app/models/internal_id.rb', line 144 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 |