Class: StatelyDB::Transaction::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/transaction/transaction.rb

Overview

Transaction coordinates sending requests and waiting for responses. Consumers should not need to interact with this class directly, but instead use the methods provided by the StatelyDB::CoreClient.

The example below demonstrates using a transaction, which accepts a block. The lines in the block are executed within the context of the transaction. The transaction is committed when the block completes successfully, OR is aborted if an exception is raised.

Examples:

result = client.transaction do |tx|
  key_path = StatelyDB::KeyPath.with('movie', 'The Shining')
  movie = tx.get(key_path:)
  tx.put(item: movie)
end

Defined Under Namespace

Classes: Result

Instance Method Summary collapse

Constructor Details

#initialize(stub:, store_id:, schema:) ⇒ Transaction

Initialize a new Transaction

Parameters:

  • stub (Stately::Db::DatabaseService::Stub)

    a StatelyDB gRPC stub

  • store_id (Integer)

    the StatelyDB Store to transact against

  • schema (StatelyDB::Schema)

    the schema to use for marshalling and unmarshalling Items



48
49
50
51
52
53
54
55
56
# File 'lib/transaction/transaction.rb', line 48

def initialize(stub:, store_id:, schema:)
  @stub = stub
  @store_id = store_id
  @schema = schema
  @is_transaction_open = false

  # A queue of outbound requests
  @outgoing_requests = StatelyDB::Transaction::Queue.new
end

Instance Method Details

#begin_list(prefix, limit: 100, sort_property: nil, sort_direction: :ascending) ⇒ (Array<StatelyDB::Item>, Stately::Db::ListToken)

Begin listing Items from a StatelyDB Store at the given prefix.

Example:

client.data.transaction do |txn|
  (items, token) = txn.begin_list("/ItemType-identifier")
  (items, token) = txn.continue_list(token)
end

Parameters:

  • prefix (String)

    the prefix to list

  • limit (Integer) (defaults to: 100)

    the maximum number of items to return

  • sort_property (String) (defaults to: nil)

    the property to sort by

  • sort_direction (Symbol) (defaults to: :ascending)

    the direction to sort by (:ascending or :descending)

Returns:



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/transaction/transaction.rb', line 334

def begin_list(prefix,
               limit: 100,
               sort_property: nil,
               sort_direction: :ascending)
  sort_direction = case sort_direction
                   when :ascending
                     0
                   else
                     1
                   end
  req = Stately::Db::TransactionRequest.new(
    begin_list: Stately::Db::TransactionBeginList.new(
      key_path_prefix: String(prefix),
      limit:,
      sort_property:,
      sort_direction:
    )
  )
  do_list_request_response(req)
end

#continue_list(token, continue_direction: :forward) ⇒ (Array<StatelyDB::Item>, Stately::Db::ListToken)

Continue listing Items from a StatelyDB Store using a token.

Example:

client.data.transaction do |txn|
  (items, token) = txn.begin_list("/foo")
  (items, token) = txn.continue_list(token)
end

Parameters:

  • token (Stately::Db::ListToken)

    the token to continue from

  • continue_direction (Symbol) (defaults to: :forward)

    the direction to continue by (:forward or :backward)

Returns:



366
367
368
369
370
371
372
373
374
375
376
# File 'lib/transaction/transaction.rb', line 366

def continue_list(token, continue_direction: :forward)
  continue_direction = continue_direction == :forward ? 0 : 1

  req = Stately::Db::TransactionRequest.new(
    continue_list: Stately::Db::TransactionContinueList.new(
      token_data: token.token_data,
      direction: continue_direction
    )
  )
  do_list_request_response(req)
end

#delete(*key_paths) ⇒ void

This method returns an undefined value.

Delete up to 50 Items from a StatelyDB Store at the given key_paths. Results are not returned until the transaction is committed and will be available in the Result object returned by commit.

Example:

client.data.transaction do |txn|
  txn.delete("/ItemType-identifier", "/ItemType-identifier2")
end

Parameters:

  • key_paths (String, Array<String>)

    the paths to the items. Max 50 key paths.



310
311
312
313
314
315
316
317
318
319
# File 'lib/transaction/transaction.rb', line 310

def delete(*key_paths)
  key_paths = Array(key_paths).flatten
  req = Stately::Db::TransactionRequest.new(
    delete_items: Stately::Db::TransactionDelete.new(
      deletes: key_paths.map { |key_path| Stately::Db::DeleteItem.new(key_path: String(key_path)) }
    )
  )
  request_only(req)
  nil
end

#get(key_path) ⇒ StatelyDB::Item, NilClass

Fetch Items from a StatelyDB Store at the given key_path. Note that Items need to exist before being retrieved inside a transaction.

Examples:

client.data.transaction do |txn|
  item = txn.get("/ItemType-identifier")
end

Parameters:

  • key_path (String)

    the path to the item

Returns:

Raises:

  • (StatelyDB::Error::InvalidParameters)

    if the parameters are invalid

  • (StatelyDB::Error::NotFound)

    if the item is not found



189
190
191
192
193
194
# File 'lib/transaction/transaction.rb', line 189

def get(key_path)
  resp = get_batch(key_path)

  # Always return a single Item.
  resp.first
end

#get_batch(*key_paths) ⇒ Array<StatelyDB::Item>

Fetch a batch of up to 100 Items from a StatelyDB Store at the given key_paths. Note that Items need to exist before being retrieved inside a transaction.

key paths. Example:

client.data.transaction do |txn|
  items = txn.get_batch("/foo", "/bar")
end

Parameters:

  • key_paths (String, Array<String>)

    the paths to the items. Max 100

Returns:

Raises:

  • (StatelyDB::Error::InvalidParameters)

    if the parameters are invalid

  • (StatelyDB::Error::NotFound)

    if the item is not found



210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/transaction/transaction.rb', line 210

def get_batch(*key_paths)
  key_paths = Array(key_paths).flatten
  req = Stately::Db::TransactionRequest.new(
    get_items: Stately::Db::TransactionGet.new(
      gets: key_paths.map { |key_path| Stately::Db::GetItem.new(key_path: String(key_path)) }
    )
  )
  resp = request_response(req).get_results

  resp.items.map do |result|
    @schema.unmarshal_item(stately_item: result)
  end
end

#put(item, must_not_exist: false) ⇒ String, Integer

Put a single Item into a StatelyDB store. Results are not returned until the transaction is committed and will be available in the Result object returned by commit. An identifier for the item will be returned while inside the transaction block.

results.puts.each do |result|
  puts result.key_path
end

Examples:

results = client.data.transaction do |txn|
  txn.put(my_item)
end

Parameters:

  • item (StatelyDB::Item)

    the item to store

  • must_not_exist (Boolean) (defaults to: false)

    A condition that indicates this item must not already exist at any of its key paths. If there is already an item at one of those paths, the Put operation will fail with a “ConditionalCheckFailed” error. Note that if the item has an ‘initialValue` field in its key, that initial value will automatically be chosen not to conflict with existing items, so this condition only applies to key paths that do not contain the `initialValue` field.

Returns:

  • (String, Integer)

    the id of the item



245
246
247
248
# File 'lib/transaction/transaction.rb', line 245

def put(item, must_not_exist: false)
  resp = put_batch({ item:, must_not_exist: })
  resp.first
end

#put_batch(*items) ⇒ Array<StatelyDB::UUID, String, Integer, nil>

Put a batch of up to 50 Items into a StatelyDB Store. Results are not returned until the transaction is committed and will be available in the Result object returned by commit. A list of identifiers for the items will be returned while inside the transaction block.

50 items.

results.puts.each do |result|
  puts result.key_path
end

Examples:

results = client.data.transaction do |txn|
  txn.put_batch(item1, item2)
end

Parameters:

Returns:



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/transaction/transaction.rb', line 266

def put_batch(*items)
  puts = Array(items).flatten.map do |input|
    if input.is_a?(Hash)
      item = input[:item]
      Stately::Db::PutItem.new(
        item: item.send("marshal_stately"),
        must_not_exist: input[:must_not_exist]
      )
    else
      Stately::Db::PutItem.new(
        item: input.send("marshal_stately")
      )
    end
  end
  req = Stately::Db::TransactionRequest.new(
    put_items: Stately::Db::TransactionPut.new(
      puts:
    )
  )

  resp = request_response(req).put_ack
  resp.generated_ids.map do |generated_id|
    case generated_id.value
    when :bytes
      StatelyDB::UUID.valid_uuid?(generated_id.bytes) ? StatelyDB::UUID.parse(generated_id.bytes) : generated_id.bytes
    when :uint
      generated_id.uint
    else # rubocop:disable Style/EmptyElse
      # An empty identifier is sent in the transaction Put response if an initialValue is not set
      nil
    end
  end
end