Module: AtomicInternalId

Extended by:
ActiveSupport::Concern
Included in:
AlertManagement::Alert, Ci::Pipeline, Deployment, DesignManagement::Design, Issue, MergeRequest, Milestone, Ml::Candidate, Ml::Experiment, Operations::FeatureFlag, Operations::FeatureFlags::UserList
Defined in:
app/models/concerns/atomic_internal_id.rb

Overview

Include atomic internal id generation scheme for a model

This allows us to atomically generate internal ids that are unique within a given scope.

For example, let’s generate internal ids for Issue per Project: “‘ class Issue < ApplicationRecord

has_internal_id :iid, scope: :project, init: ->(s) { s.project.issues.maximum(:iid) }

end “‘

This generates unique internal ids per project for newly created issues. The generated internal id is saved in the ‘iid` attribute of `Issue`.

This concern uses InternalId records to facilitate atomicity. In the absence of a record for the given scope, one will be created automatically. In this situation, the ‘init` block is called to calculate the initial value. In the example above, we calculate the maximum `iid` of all issues within the given project.

Note that a model may have more than one internal id associated with possibly different scopes.

Defined Under Namespace

Classes: Supply

Constant Summary collapse

MissingValueError =
Class.new(StandardError)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.group_init(klass, column_name = :iid) ⇒ Object



236
237
238
239
240
241
242
243
244
# File 'app/models/concerns/atomic_internal_id.rb', line 236

def self.group_init(klass, column_name = :iid)
  ->(instance, scope) do
    if instance
      klass.where(group_id: instance.group_id).maximum(column_name)
    elsif scope.present?
      klass.where(group: scope[:namespace]).maximum(column_name)
    end
  end
end

.namespace_init(klass, column_name = :iid) ⇒ Object



246
247
248
249
250
251
252
253
254
# File 'app/models/concerns/atomic_internal_id.rb', line 246

def self.namespace_init(klass, column_name = :iid)
  ->(instance, scope) do
    if instance
      klass.where(namespace_id: instance.namespace_id).maximum(column_name)
    elsif scope.present?
      klass.where(**scope).maximum(column_name)
    end
  end
end

.project_init(klass, column_name = :iid) ⇒ Object



226
227
228
229
230
231
232
233
234
# File 'app/models/concerns/atomic_internal_id.rb', line 226

def self.project_init(klass, column_name = :iid)
  ->(instance, scope) do
    if instance
      klass.default_scoped.where(project_id: instance.project_id).maximum(column_name)
    elsif scope.present?
      klass.default_scoped.where(**scope).maximum(column_name)
    end
  end
end

.scope_attrs(scope_value) ⇒ Object



208
209
210
# File 'app/models/concerns/atomic_internal_id.rb', line 208

def self.scope_attrs(scope_value)
  { scope_value.class.table_name.singularize.to_sym => scope_value } if scope_value
end

.scope_usage(including_class) ⇒ Object



222
223
224
# File 'app/models/concerns/atomic_internal_id.rb', line 222

def self.scope_usage(including_class)
  including_class.table_name.to_sym
end

Instance Method Details

#internal_id_read_scope(scope) ⇒ Object



256
257
258
# File 'app/models/concerns/atomic_internal_id.rb', line 256

def internal_id_read_scope(scope)
  association(scope).reader
end

#internal_id_scope_attrs(scope) ⇒ Object



212
213
214
215
216
# File 'app/models/concerns/atomic_internal_id.rb', line 212

def internal_id_scope_attrs(scope)
  scope_value = internal_id_read_scope(scope)

  ::AtomicInternalId.scope_attrs(scope_value)
end

#internal_id_scope_usageObject



218
219
220
# File 'app/models/concerns/atomic_internal_id.rb', line 218

def internal_id_scope_usage
  ::AtomicInternalId.scope_usage(self.class)
end