Class: LaunchDarkly::Impl::Integrations::DynamoDB::DynamoDBFeatureStoreCore

Inherits:
Object
  • Object
show all
Defined in:
lib/ldclient-rb/impl/integrations/dynamodb_impl.rb

Overview

Internal implementation of the DynamoDB feature store, intended to be used with CachingStoreWrapper.

Since:

  • 5.5.0

Constant Summary collapse

PARTITION_KEY =

Since:

  • 5.5.0

"namespace"
SORT_KEY =

Since:

  • 5.5.0

"key"
VERSION_ATTRIBUTE =

Since:

  • 5.5.0

"version"
ITEM_JSON_ATTRIBUTE =

Since:

  • 5.5.0

"item"

Instance Method Summary collapse

Constructor Details

#initialize(table_name, opts) ⇒ DynamoDBFeatureStoreCore

Returns a new instance of DynamoDBFeatureStoreCore.

Since:

  • 5.5.0



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/ldclient-rb/impl/integrations/dynamodb_impl.rb', line 29

def initialize(table_name, opts)
  if !AWS_SDK_ENABLED
    raise RuntimeError.new("can't use DynamoDB feature store without the aws-sdk or aws-sdk-dynamodb gem")
  end

  @table_name = table_name
  @prefix = opts[:prefix]
  @logger = opts[:logger] || Config.default_logger

  if !opts[:existing_client].nil?
    @client = opts[:existing_client]
  else
    @client = Aws::DynamoDB::Client.new(opts[:dynamodb_opts] || {})
  end

  @logger.info("DynamoDBFeatureStore: using DynamoDB table \"#{table_name}\"")
end

Instance Method Details

#get_all_internal(kind) ⇒ Object

Since:

  • 5.5.0



83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ldclient-rb/impl/integrations/dynamodb_impl.rb', line 83

def get_all_internal(kind)
  items_out = {}
  req = make_query_for_kind(kind)
  while true
    resp = @client.query(req)
    resp.items.each do |item|
      item_out = unmarshal_item(kind, item)
      items_out[item_out[:key].to_sym] = item_out
    end
    break if resp.last_evaluated_key.nil? || resp.last_evaluated_key.length == 0
    req.exclusive_start_key = resp.last_evaluated_key
  end
  items_out
end

#get_internal(kind, key) ⇒ Object

Since:

  • 5.5.0



78
79
80
81
# File 'lib/ldclient-rb/impl/integrations/dynamodb_impl.rb', line 78

def get_internal(kind, key)
  resp = get_item_by_keys(namespace_for_kind(kind), key)
  unmarshal_item(kind, resp.item)
end

#init_internal(all_data) ⇒ Object

Since:

  • 5.5.0



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ldclient-rb/impl/integrations/dynamodb_impl.rb', line 47

def init_internal(all_data)
  # Start by reading the existing keys; we will later delete any of these that weren't in all_data.
  unused_old_keys = read_existing_keys(all_data.keys)

  requests = []
  num_items = 0

  # Insert or update every provided item
  all_data.each do |kind, items|
    items.values.each do |item|
      requests.push({ put_request: { item: marshal_item(kind, item) } })
      unused_old_keys.delete([ namespace_for_kind(kind), item[:key] ])
      num_items = num_items + 1
    end
  end

  # Now delete any previously existing items whose keys were not in the current data
  unused_old_keys.each do |tuple|
    del_item = make_keys_hash(tuple[0], tuple[1])
    requests.push({ delete_request: { key: del_item } })
  end
    
  # Now set the special key that we check in initialized_internal?
  inited_item = make_keys_hash(inited_key, inited_key)
  requests.push({ put_request: { item: inited_item } })

  DynamoDBUtil.batch_write_requests(@client, @table_name, requests)

  @logger.info { "Initialized table #{@table_name} with #{num_items} items" }
end

#initialized_internal?Boolean

Returns:

  • (Boolean)

Since:

  • 5.5.0



122
123
124
125
# File 'lib/ldclient-rb/impl/integrations/dynamodb_impl.rb', line 122

def initialized_internal?
  resp = get_item_by_keys(inited_key, inited_key)
  !resp.item.nil? && resp.item.length > 0
end

#stopObject

Since:

  • 5.5.0



127
128
129
# File 'lib/ldclient-rb/impl/integrations/dynamodb_impl.rb', line 127

def stop
  # AWS client doesn't seem to have a close method
end

#upsert_internal(kind, new_item) ⇒ Object

Since:

  • 5.5.0



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/ldclient-rb/impl/integrations/dynamodb_impl.rb', line 98

def upsert_internal(kind, new_item)
  encoded_item = marshal_item(kind, new_item)
  begin
    @client.put_item({
      table_name: @table_name,
      item: encoded_item,
      condition_expression: "attribute_not_exists(#namespace) or attribute_not_exists(#key) or :version > #version",
      expression_attribute_names: {
        "#namespace" => PARTITION_KEY,
        "#key" => SORT_KEY,
        "#version" => VERSION_ATTRIBUTE
      },
      expression_attribute_values: {
        ":version" => new_item[:version]
      }
    })
    new_item
  rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
    # The item was not updated because there's a newer item in the database.
    # We must now read the item that's in the database and return it, so CachingStoreWrapper can cache it.
    get_internal(kind, new_item[:key])
  end
end