Skip to content

Commit

Permalink
Remove Sendable conformance from entry types (#44)
Browse files Browse the repository at this point in the history
Remove Sendable conformance from PolymorphicWriteEntry and PolymorphicTransactionConstraintEntry
  • Loading branch information
tachyonics authored Sep 19, 2024
1 parent 416a7e8 commit d86787e
Show file tree
Hide file tree
Showing 16 changed files with 125 additions and 157 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ and similarly for polymorphic queries, most conveniently by using the `@Polymorp
typealias TestTypeBWriteEntry = StandardWriteEntry<TestTypeB>

@PolymorphicWriteEntry
enum TestPolymorphicWriteEntry: Sendable {
enum TestPolymorphicWriteEntry {
case testTypeA(TestTypeAWriteEntry)
case testTypeB(TestTypeBWriteEntry)
}
Expand Down Expand Up @@ -407,7 +407,7 @@ typealias TestTypeAStandardTransactionConstraintEntry = StandardTransactionConst
typealias TestTypeBStandardTransactionConstraintEntry = StandardTransactionConstraintEntry<TestTypeB>

@PolymorphicTransactionConstraintEntry
enum TestPolymorphicTransactionConstraintEntry: Sendable {
enum TestPolymorphicTransactionConstraintEntry {
case testTypeA(TestTypeAStandardTransactionConstraintEntry)
case testTypeB(TestTypeBStandardTransactionConstraintEntry)
}
Expand All @@ -428,7 +428,7 @@ about failed transactions. This is enabled by default when using the `@Polymorph

```swift
@PolymorphicWriteEntry(passCompositePrimaryKey: false)
enum TestPolymorphicWriteEntry: Sendable {
enum TestPolymorphicWriteEntry {
case testTypeA(TestTypeAWriteEntry)
case testTypeB(TestTypeBWriteEntry)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,12 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
_ = try await dynamodb.executeTransaction(input: transactionInput)
}

private func writeTransactionItems(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry]) async throws
private func getExecuteTransactionInput(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry]) throws -> ExecuteTransactionInput?
{
// if there are no items, there is nothing to update
guard entries.count > 0 else {
return
return nil
}

let context = StandardPolymorphicWriteEntryContext<AWSDynamoDBPolymorphicWriteEntryTransform,
Expand All @@ -199,9 +199,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
return DynamoDBClientTypes.ParameterizedStatement(statement: statement)
}

let transactionInput = ExecuteTransactionInput(transactStatements: entryStatements + requiredItemsStatements)

_ = try await dynamodb.executeTransaction(input: transactionInput)
return ExecuteTransactionInput(transactStatements: entryStatements + requiredItemsStatements)
}

func transactWrite(_ entries: [WriteEntry<some Any, some Any>]) async throws {
Expand All @@ -216,16 +214,29 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
retriesRemaining: self.retryConfiguration.numRetries)
}

func polymorphicTransactWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
func polymorphicTransactWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
let noConstraints: [EmptyPolymorphicTransactionConstraintEntry] = []
return try await self.polymorphicTransactWrite(entries, constraints: noConstraints,
retriesRemaining: self.retryConfiguration.numRetries)

guard let transactionInput = try getExecuteTransactionInput(entries, constraints: noConstraints) else {
// nothing to do
return
}
let inputKeys = entries.map(\.compositePrimaryKey)

try await self.polymorphicTransactWrite(transactionInput, inputKeys: inputKeys,
retriesRemaining: self.retryConfiguration.numRetries)
}

func polymorphicTransactWrite(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry]) async throws
_ entries: sending [some PolymorphicWriteEntry], constraints: sending [some PolymorphicTransactionConstraintEntry]) async throws
{
try await self.polymorphicTransactWrite(entries, constraints: constraints,
guard let transactionInput = try getExecuteTransactionInput(entries, constraints: constraints) else {
// nothing to do
return
}
let inputKeys = entries.map(\.compositePrimaryKey) + constraints.map(\.compositePrimaryKey)

try await self.polymorphicTransactWrite(transactionInput, inputKeys: inputKeys,
retriesRemaining: self.retryConfiguration.numRetries)
}

Expand Down Expand Up @@ -336,31 +347,24 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
return try await self.transactWrite(entries, constraints: constraints, retriesRemaining: retriesRemaining - 1)
}

private func polymorphicTransactWrite(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry],
retriesRemaining: Int) async throws
{
let entryCount = entries.count + constraints.count

if entryCount > AWSDynamoDBLimits.maximumUpdatesPerTransactionStatement {
throw DynamoDBTableError.transactionSizeExceeded(attemptedSize: entryCount,
private func polymorphicTransactWrite(_ transactionInput: ExecuteTransactionInput, inputKeys: [StandardCompositePrimaryKey?], retriesRemaining: Int) async throws {
if inputKeys.count > AWSDynamoDBLimits.maximumUpdatesPerTransactionStatement {
throw DynamoDBTableError.transactionSizeExceeded(attemptedSize: inputKeys.count,
maximumSize: AWSDynamoDBLimits.maximumUpdatesPerTransactionStatement)
}

let result: Swift.Result<Void, DynamoDBTableError>
do {
try await self.writeTransactionItems(entries, constraints: constraints)
_ = try await dynamodb.executeTransaction(input: transactionInput)

result = .success(())
} catch let exception as TransactionCanceledException {
guard let cancellationReasons = exception.properties.cancellationReasons else {
throw DynamoDBTableError.transactionCanceled(reasons: [])
}

let keys = entries.map(\.compositePrimaryKey) + constraints.map(\.compositePrimaryKey)

var isTransactionConflict = false
let reasons = try zip(cancellationReasons, keys).compactMap { cancellationReason, entryKey -> DynamoDBTableError? in
let reasons = try zip(cancellationReasons, inputKeys).compactMap { cancellationReason, entryKey -> DynamoDBTableError? in
let key: StandardCompositePrimaryKey?
if let item = cancellationReason.item {
key = try DynamoDBDecoder().decode(.m(item))
Expand All @@ -383,7 +387,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
return DynamoDBTableError.duplicateItem(partitionKey: partitionKey, sortKey: sortKey,
message: cancellationReason.message)
case "ItemCollectionSizeLimitExceeded":
return DynamoDBTableError.transactionSizeExceeded(attemptedSize: entryCount,
return DynamoDBTableError.transactionSizeExceeded(attemptedSize: inputKeys.count,
maximumSize: AWSDynamoDBLimits.maximumUpdatesPerTransactionStatement)
case "TransactionConflict":
isTransactionConflict = true
Expand All @@ -403,13 +407,13 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
}

if isTransactionConflict, retriesRemaining > 0 {
return try await retryPolymorphicTransactWrite(entries, constraints: constraints, retriesRemaining: retriesRemaining)
return try await retryPolymorphicTransactWrite(transactionInput, inputKeys: inputKeys, retriesRemaining: retriesRemaining)
}

result = .failure(DynamoDBTableError.transactionCanceled(reasons: reasons))
} catch let exception as TransactionConflictException {
if retriesRemaining > 0 {
return try await retryPolymorphicTransactWrite(entries, constraints: constraints, retriesRemaining: retriesRemaining)
return try await retryPolymorphicTransactWrite(transactionInput, inputKeys: inputKeys, retriesRemaining: retriesRemaining)
}

let reason = DynamoDBTableError.transactionConflict(message: exception.message)
Expand All @@ -428,10 +432,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
}
}

private func retryPolymorphicTransactWrite(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry],
retriesRemaining: Int) async throws
{
private func retryPolymorphicTransactWrite(_ transactionInput: ExecuteTransactionInput, inputKeys: [StandardCompositePrimaryKey?], retriesRemaining: Int) async throws {
// determine the required interval
let retryInterval = Int(self.retryConfiguration.getRetryInterval(retriesRemaining: retriesRemaining))

Expand All @@ -440,7 +441,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
try await Task.sleep(nanoseconds: UInt64(retryInterval) * millisecondsToNanoSeconds)

logger.trace("Reattempting request due to remaining retries: \(retryInterval)")
return try await self.polymorphicTransactWrite(entries, constraints: constraints, retriesRemaining: retriesRemaining - 1)
return try await self.polymorphicTransactWrite(transactionInput, inputKeys: inputKeys, retriesRemaining: retriesRemaining - 1)
}

private func writeChunkedItems(_ entries: [some PolymorphicWriteEntry]) async throws {
Expand All @@ -464,7 +465,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
try self.throwOnBatchExecuteStatementErrors(response: response)
}

func polymorphicBulkWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
func polymorphicBulkWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
// BatchExecuteStatement has a maximum of 25 statements
// This function handles pagination internally.
let chunkedEntries = entries.chunked(by: AWSDynamoDBLimits.maximumUpdatesPerExecuteStatement)
Expand Down
6 changes: 3 additions & 3 deletions Sources/DynamoDBTables/DynamoDBCompositePrimaryKeyTable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public protocol DynamoDBCompositePrimaryKeyTable {
func transactWrite<AttributesType, ItemType>(_ entries: [WriteEntry<AttributesType, ItemType>]) async throws

func polymorphicTransactWrite<WriteEntryType: PolymorphicWriteEntry>(
_ entries: [WriteEntryType]) async throws
_ entries: sending [WriteEntryType]) async throws

/**
* Provides the ability to bulk write database rows in a transaction.
Expand All @@ -153,7 +153,7 @@ public protocol DynamoDBCompositePrimaryKeyTable {
_ entries: [WriteEntry<AttributesType, ItemType>], constraints: [TransactionConstraintEntry<AttributesType, ItemType>]) async throws

func polymorphicTransactWrite<WriteEntryType: PolymorphicWriteEntry, TransactionConstraintEntryType: PolymorphicTransactionConstraintEntry>(
_ entries: [WriteEntryType], constraints: [TransactionConstraintEntryType]) async throws
_ entries: sending [WriteEntryType], constraints: sending [TransactionConstraintEntryType]) async throws

/**
* Provides the ability to bulk write database rows
Expand All @@ -165,7 +165,7 @@ public protocol DynamoDBCompositePrimaryKeyTable {
func bulkWriteWithoutThrowing<AttributesType, ItemType>(_ entries: [WriteEntry<AttributesType, ItemType>]) async throws
-> Set<DynamoDBClientTypes.BatchStatementErrorCodeEnum>

func polymorphicBulkWrite<WriteEntryType: PolymorphicWriteEntry>(_ entries: [WriteEntryType]) async throws
func polymorphicBulkWrite<WriteEntryType: PolymorphicWriteEntry>(_ entries: sending [WriteEntryType]) async throws

/**
* Retrieves an item from the database table. Returns nil if the item doesn't exist.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,10 @@ public typealias ExecuteItemFilterType = @Sendable (String, String, String, Poly

public protocol InMemoryTransactionDelegate {
/**
Inject errors into a `transactWrite` call.
Inject errors into a `transactWrite` or `polymorphicTransactWrite` call.
*/
func injectErrors<AttributesType, ItemType>(
_ entries: [WriteEntry<AttributesType, ItemType>], constraints: [TransactionConstraintEntry<AttributesType, ItemType>],
table: InMemoryDynamoDBCompositePrimaryKeyTable) async throws -> [DynamoDBTableError]

/**
Inject errors into a `polymorphicTransactWrite` call.
*/
func injectErrors<WriteEntryType: PolymorphicWriteEntry,
TransactionConstraintEntryType: PolymorphicTransactionConstraintEntry>(
_ entries: [WriteEntryType], constraints: [TransactionConstraintEntryType],
table: InMemoryDynamoDBCompositePrimaryKeyTable) async throws -> [DynamoDBTableError]
func injectErrors<AttributesType>(
inputKeys: [CompositePrimaryKey<AttributesType>?], table: InMemoryDynamoDBCompositePrimaryKeyTable) async throws -> [DynamoDBTableError]
}

public struct InMemoryDynamoDBCompositePrimaryKeyTable: DynamoDBCompositePrimaryKeyTable {
Expand Down Expand Up @@ -116,30 +107,32 @@ public struct InMemoryDynamoDBCompositePrimaryKeyTable: DynamoDBCompositePrimary
constraints: [TransactionConstraintEntry<AttributesType, ItemType>]) async throws
{
// if there is a transaction delegate and it wants to inject errors
if let errors = try await transactionDelegate?.injectErrors(entries, constraints: constraints, table: self), !errors.isEmpty {
let inputKeys = entries.map(\.compositePrimaryKey) + constraints.map(\.compositePrimaryKey)
if let errors = try await transactionDelegate?.injectErrors(inputKeys: inputKeys, table: self), !errors.isEmpty {
throw DynamoDBTableError.transactionCanceled(reasons: errors)
}

return try await self.storeWrapper.bulkWrite(entries, constraints: constraints, isTransaction: true)
}

public func polymorphicTransactWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
public func polymorphicTransactWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
let noConstraints: [EmptyPolymorphicTransactionConstraintEntry] = []
return try await self.polymorphicTransactWrite(entries, constraints: noConstraints)
}

public func polymorphicTransactWrite(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry]) async throws
_ entries: sending [some PolymorphicWriteEntry], constraints: sending [some PolymorphicTransactionConstraintEntry]) async throws
{
// if there is a transaction delegate and it wants to inject errors
if let errors = try await transactionDelegate?.injectErrors(entries, constraints: constraints, table: self), !errors.isEmpty {
let inputKeys = entries.map(\.compositePrimaryKey) + constraints.map(\.compositePrimaryKey)
if let errors = try await transactionDelegate?.injectErrors(inputKeys: inputKeys, table: self), !errors.isEmpty {
throw DynamoDBTableError.transactionCanceled(reasons: errors)
}

return try await self.storeWrapper.polymorphicBulkWrite(entries, constraints: constraints, isTransaction: true)
}

public func polymorphicBulkWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
public func polymorphicBulkWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
let noConstraints: [EmptyPolymorphicTransactionConstraintEntry] = []
return try await self.storeWrapper.polymorphicBulkWrite(entries, constraints: noConstraints, isTransaction: false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ actor InMemoryDynamoDBCompositePrimaryKeyTableStore {
}

func polymorphicBulkWrite(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry],
_ entries: sending [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry],
isTransaction: Bool) throws
{
let entryCount = entries.count + constraints.count
Expand Down Expand Up @@ -664,7 +664,7 @@ extension InMemoryDynamoDBCompositePrimaryKeyTableStore {
}

func handlePolymorphicEntries(
entries: [some PolymorphicWriteEntry], isTransaction: Bool,
entries: sending [some PolymorphicWriteEntry], isTransaction: Bool,
context: StandardPolymorphicWriteEntryContext<InMemoryPolymorphicWriteEntryTransform,
InMemoryPolymorphicTransactionConstraintTransform>)
-> DynamoDBTableError?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,17 @@ public struct InMemoryDynamoDBCompositePrimaryKeyTableWithIndex<GSILogic: Dynamo
try await self.primaryTable.transactWrite(entries, constraints: constraints)
}

public func polymorphicTransactWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
public func polymorphicTransactWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
try await self.primaryTable.polymorphicTransactWrite(entries)
}

public func polymorphicTransactWrite(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry]) async throws
_ entries: sending [some PolymorphicWriteEntry], constraints: sending [some PolymorphicTransactionConstraintEntry]) async throws
{
try await self.primaryTable.polymorphicTransactWrite(entries, constraints: constraints)
}

public func polymorphicBulkWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
public func polymorphicBulkWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
try await self.primaryTable.polymorphicBulkWrite(entries)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/DynamoDBTables/PolymorphicWriteEntry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public protocol PolymorphicTransactionConstraintTransform {

// Conforming types are provided by the application to express the different possible write entries
// and how they can be converted to the table-provided transform type.
public protocol PolymorphicWriteEntry: Sendable {
public protocol PolymorphicWriteEntry {
func handle<Context: PolymorphicWriteEntryContext>(context: Context) throws -> Context.WriteEntryTransformType

var compositePrimaryKey: StandardCompositePrimaryKey? { get }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,17 @@ public class SimulateConcurrencyDynamoDBCompositePrimaryKeyTable: DynamoDBCompos
try await self.wrappedDynamoDBTable.transactWrite(entries, constraints: constraints)
}

public func polymorphicTransactWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
public func polymorphicTransactWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
try await self.wrappedDynamoDBTable.polymorphicTransactWrite(entries)
}

public func polymorphicTransactWrite(
_ entries: [some PolymorphicWriteEntry], constraints: [some PolymorphicTransactionConstraintEntry]) async throws
_ entries: sending [some PolymorphicWriteEntry], constraints: sending [some PolymorphicTransactionConstraintEntry]) async throws
{
try await self.wrappedDynamoDBTable.polymorphicTransactWrite(entries, constraints: constraints)
}

public func polymorphicBulkWrite(_ entries: [some PolymorphicWriteEntry]) async throws {
public func polymorphicBulkWrite(_ entries: sending [some PolymorphicWriteEntry]) async throws {
try await self.wrappedDynamoDBTable.polymorphicBulkWrite(entries)
}

Expand Down
Loading

0 comments on commit d86787e

Please sign in to comment.