Class: ActiveStorage::AwsRecord::Schema

Inherits:
Object
  • Object
show all
Defined in:
lib/active_storage/aws_record/schema.rb

Overview

The resolved physical layout of the single table, discovered once at boot from describe_table (with optional config overrides) and cached read-only thereafter. Two modes, chosen by the range key’s type:

  • :range (range key is String “S”) — the gem writes its #-composite adjacency keys straight into the table’s (partition, sort) attributes. All reads are on the base table and strongly consistent. No GSI.

  • :index (range key is Number “N”) — composite strings cannot live in a numeric range key, so each item is keyed by a unique item_id plus a constant 0 range, and the adjacency keys move to a string-keyed GSI that serves listing queries (eventually consistent). Point lookups, the refcount, and the foreign-key guard stay on the base table (strong).

The partition key must be String in both modes (it stores item_id/key strings); a numeric partition key is rejected.

Constant Summary collapse

RESERVED_ATTRIBUTE_NAMES =

DynamoDB attribute names the gem stores for its own logical attributes. Detected key attributes must not collide with these (see guard_names!).

%w[
  as_id as_key as_filename as_content_type as_byte_size as_checksum
  as_metadata as_service_name as_created_at as_attachments_count
  as_record_type as_record_id as_name as_blob_id as_variation_digest
  as_entity
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(table, config) ⇒ Schema

Returns a new instance of Schema.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/active_storage/aws_record/schema.rb', line 49

def initialize(table, config)
  types = table.attribute_definitions.each_with_object({}) { |d, h| h[d.attribute_name] = d.attribute_type }

  @partition_attr = key_name(table.key_schema, 'HASH')
  @sort_attr      = key_name(table.key_schema, 'RANGE')

  validate!(types)

  case types[@sort_attr]
  when 'S'
    @mode = :range
  when 'N'
    @mode = :index
    resolve_index!(table, config, types)
  else
    raise ConfigurationError, "Range key #{@sort_attr.inspect} must be type String (S) or Number (N), " \
      "got #{types[@sort_attr].inspect}."
  end

  self.class.guard_names!(self)
end

Instance Attribute Details

#index_nameObject (readonly)

Returns the value of attribute index_name.



28
29
30
# File 'lib/active_storage/aws_record/schema.rb', line 28

def index_name
  @index_name
end

#index_partition_attrObject (readonly)

Returns the value of attribute index_partition_attr.



28
29
30
# File 'lib/active_storage/aws_record/schema.rb', line 28

def index_partition_attr
  @index_partition_attr
end

#index_sort_attrObject (readonly)

Returns the value of attribute index_sort_attr.



28
29
30
# File 'lib/active_storage/aws_record/schema.rb', line 28

def index_sort_attr
  @index_sort_attr
end

#modeObject (readonly)

Returns the value of attribute mode.



28
29
30
# File 'lib/active_storage/aws_record/schema.rb', line 28

def mode
  @mode
end

#partition_attrObject (readonly)

Returns the value of attribute partition_attr.



28
29
30
# File 'lib/active_storage/aws_record/schema.rb', line 28

def partition_attr
  @partition_attr
end

#sort_attrObject (readonly)

Returns the value of attribute sort_attr.



28
29
30
# File 'lib/active_storage/aws_record/schema.rb', line 28

def sort_attr
  @sort_attr
end

Class Method Details

.describe(client, table_name) ⇒ Object



42
43
44
45
46
47
# File 'lib/active_storage/aws_record/schema.rb', line 42

def self.describe(client, table_name)
  client.describe_table(table_name: table_name).table
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
  raise ConfigurationError, "DynamoDB table #{table_name.inspect} does not exist. " \
    'Create it first, or set config.manage_table = true in development.'
end

.discover(client, config) ⇒ Schema

Inspect the live table and resolve the layout.

Parameters:

Returns:

Raises:



37
38
39
40
# File 'lib/active_storage/aws_record/schema.rb', line 37

def self.discover(client, config)
  table = describe(client, config.table_name)
  new(table, config)
end

.guard_names!(schema) ⇒ Object

Reject a table whose key (or index key) attribute names collide with the gem’s stored attributes — aws-record cannot map two attributes to one DynamoDB name.



137
138
139
140
141
142
143
144
145
# File 'lib/active_storage/aws_record/schema.rb', line 137

def self.guard_names!(schema)
  names = [schema.partition_attr, schema.sort_attr,
           schema.index_partition_attr, schema.index_sort_attr,].compact
  clash = names & RESERVED_ATTRIBUTE_NAMES
  unless clash.empty?
    raise ConfigurationError, "Key attribute name(s) #{clash.inspect} collide with attributes the gem " \
      'stores. Use different key attribute names for this table.'
  end
end

Instance Method Details

#index_mode?Boolean

Returns true in Mode B (numeric range key, GSI adjacency).

Returns:

  • (Boolean)

    true in Mode B (numeric range key, GSI adjacency).



75
# File 'lib/active_storage/aws_record/schema.rb', line 75

def index_mode? = @mode == :index

#range_mode?Boolean

Returns true in Mode A (string range key, base-table adjacency).

Returns:

  • (Boolean)

    true in Mode A (string range key, base-table adjacency).



72
# File 'lib/active_storage/aws_record/schema.rb', line 72

def range_mode? = @mode == :range