Skip to content

Remove Sendable conformance from entry types #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading