Skip to content

Commit c45677f

Browse files
authored
Use correct safe collection concurrency primatives. (#87)
* Use correct safe collection concurrency primatives. * Fix linting.
1 parent 0d92ac5 commit c45677f

File tree

34 files changed

+494
-194
lines changed

34 files changed

+494
-194
lines changed

.swiftformat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Swift version
2-
--swiftversion 5.8
2+
--swiftversion 6.1
33

44
# file options
55
--exclude .build

Package.resolved

Lines changed: 3 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,8 @@ let package = Package(
4343
.package(url: "https://github.com/awslabs/aws-sdk-swift.git", from: "1.0.0"),
4444
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
4545
.package(url: "https://github.com/apple/swift-metrics.git", "1.0.0" ..< "3.0.0"),
46-
.package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit", from: "0.2.0"),
47-
.package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.53.9"),
4846
.package(url: "https://github.com/apple/swift-syntax", from: "601.0.0"),
49-
.package(url: "https://github.com/tachyonics/smockable", from: "0.3.0"),
47+
.package(url: "https://github.com/tachyonics/smockable", from: "0.4.0"),
5048
],
5149
targets: [
5250
.macro(name: "DynamoDBTablesMacros", dependencies: [
@@ -59,7 +57,6 @@ let package = Package(
5957
.product(name: "Logging", package: "swift-log"),
6058
.product(name: "Metrics", package: "swift-metrics"),
6159
.product(name: "AWSDynamoDB", package: "aws-sdk-swift"),
62-
.product(name: "CollectionConcurrencyKit", package: "CollectionConcurrencyKit"),
6360
.product(name: "Smockable", package: "smockable"),
6461
],
6562
swiftSettings: swiftSettings),

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+bulkUpdateSupport.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ enum AttributeDifference: Equatable {
3636
var path: String {
3737
switch self {
3838
case .update(path: let path, value: _):
39-
return path
39+
path
4040
case let .remove(path: path):
41-
return path
41+
path
4242
case .listAppend(path: let path, value: _):
43-
return path
43+
path
4444
}
4545
}
4646
}
@@ -241,17 +241,17 @@ extension GenericAWSDynamoDBCompositePrimaryKeyTable {
241241

242242
private func combinePath(basePath: String?, newComponent: String) -> String {
243243
if let basePath {
244-
return "\(basePath).\"\(newComponent)\""
244+
"\(basePath).\"\(newComponent)\""
245245
} else {
246-
return "\"\(newComponent)\""
246+
"\"\(newComponent)\""
247247
}
248248
}
249249

250250
private func updateAttribute(newPath: String, attribute: DynamoDBClientTypes.AttributeValue) throws -> [AttributeDifference] {
251251
if let newValue = try getFlattenedAttribute(attribute: attribute) {
252-
return [.update(path: newPath, value: newValue)]
252+
[.update(path: newPath, value: newValue)]
253253
} else {
254-
return [.remove(path: newPath)]
254+
[.remove(path: newPath)]
255255
}
256256
}
257257

@@ -310,9 +310,9 @@ extension GenericAWSDynamoDBCompositePrimaryKeyTable {
310310
/// single quote by doubling it. E.g. 'foo'bar' becomes 'foo''bar'.
311311
private func sanitizeString(_ string: String) -> String {
312312
if self.tableConfiguration.escapeSingleQuoteInPartiQL {
313-
return string.replacingOccurrences(of: "'", with: "''")
313+
string.replacingOccurrences(of: "'", with: "''")
314314
} else {
315-
return string
315+
string
316316
}
317317
}
318318
}

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+execute.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,16 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
4040
{
4141
let attributesFilterString = attributesFilter?.joined(separator: ", ") ?? "*"
4242

43-
let partitionWhereClause: String
44-
if partitionKeys.count == 1 {
45-
partitionWhereClause = "\(partitionKeyAttributeName)='\(partitionKeys[0])'"
43+
let partitionWhereClause = if partitionKeys.count == 1 {
44+
"\(partitionKeyAttributeName)='\(partitionKeys[0])'"
4645
} else {
47-
partitionWhereClause = "\(partitionKeyAttributeName) IN ['\(partitionKeys.joined(separator: "', '"))']"
46+
"\(partitionKeyAttributeName) IN ['\(partitionKeys.joined(separator: "', '"))']"
4847
}
4948

50-
let whereClausePostfix: String
51-
if let additionalWhereClause {
52-
whereClausePostfix = " \(additionalWhereClause)"
49+
let whereClausePostfix = if let additionalWhereClause {
50+
" \(additionalWhereClause)"
5351
} else {
54-
whereClausePostfix = ""
52+
""
5553
}
5654

5755
return """
@@ -125,7 +123,7 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
125123
nextToken: nil)
126124
}
127125

128-
return itemLists.flatMap { $0 }
126+
return itemLists.flatMap(\.self)
129127
}
130128

131129
func execute<AttributesType, ItemType, TimeToLiveAttributesType>(
@@ -193,7 +191,7 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
193191
nextToken: nil)
194192
}
195193

196-
return itemLists.flatMap { $0 }
194+
return itemLists.flatMap(\.self)
197195
}
198196

199197
// function to return a future with the results of an execute call and all future paginated calls
@@ -320,7 +318,7 @@ extension [DynamoDBTableError] {
320318

321319
// iterate through all errors
322320
return self.compactMap { error in
323-
return switch error {
321+
switch error {
324322
case .accessDenied:
325323
canPassThrough(state: &seenAccessDenied) ? error : nil
326324
case .internalServerError:

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+getItems.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
4242
monitors the unprocessed items returned in the response from DynamoDB and uses an exponential backoff algorithm to retry those items using
4343
the same retry configuration as the underlying DynamoDB client.
4444
*/
45-
private class GetItemsRetriable<AttributesType: PrimaryKeyAttributes, ItemType: Codable, TimeToLiveAttributesType: TimeToLiveAttributes, DynamoClient: DynamoDBClientProtocol> {
45+
private class GetItemsRetriable<AttributesType: PrimaryKeyAttributes, ItemType: Codable & Sendable, TimeToLiveAttributesType: TimeToLiveAttributes, DynamoClient: DynamoDBClientProtocol> {
4646
typealias OutputType = [CompositePrimaryKey<AttributesType>: TypedTTLDatabaseItem<AttributesType, ItemType, TimeToLiveAttributesType>]
4747

4848
let dynamodb: DynamoClient

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+updateItems.swift

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
//
2626

2727
import AWSDynamoDB
28-
import CollectionConcurrencyKit
2928
import Foundation
3029
import Logging
3130

@@ -38,7 +37,7 @@ public enum AWSDynamoDBLimits {
3837
public static let maxStatementLength = 8192
3938
}
4039

41-
private struct AWSDynamoDBPolymorphicWriteEntryTransform<Client: DynamoDBClientProtocol>: PolymorphicWriteEntryTransform {
40+
private struct AWSDynamoDBPolymorphicWriteEntryTransform<Client: DynamoDBClientProtocol & Sendable>: PolymorphicWriteEntryTransform {
4241
typealias TableType = GenericAWSDynamoDBCompositePrimaryKeyTable<Client>
4342

4443
let statement: String
@@ -48,12 +47,12 @@ private struct AWSDynamoDBPolymorphicWriteEntryTransform<Client: DynamoDBClientP
4847
}
4948
}
5049

51-
private struct AWSDynamoDBPolymorphicTransactionConstraintTransform<Client: DynamoDBClientProtocol>: PolymorphicTransactionConstraintTransform {
50+
private struct AWSDynamoDBPolymorphicTransactionConstraintTransform<Client: DynamoDBClientProtocol & Sendable>: PolymorphicTransactionConstraintTransform {
5251
typealias TableType = GenericAWSDynamoDBCompositePrimaryKeyTable<Client>
5352

5453
let statement: String
5554

56-
init(_ entry: TransactionConstraintEntry<some PrimaryKeyAttributes, some Codable, some TimeToLiveAttributes>,
55+
init(_ entry: TransactionConstraintEntry<some PrimaryKeyAttributes, some Codable & Sendable, some TimeToLiveAttributes>,
5756
table: TableType) throws
5857
{
5958
self.statement = try table.entryToStatement(entry)
@@ -75,34 +74,32 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
7574
internal func entryToStatement(
7675
_ entry: WriteEntry<some Any, some Any, some Any>) throws -> String
7776
{
78-
let statement: String
79-
switch entry {
77+
let statement: String = switch entry {
8078
case let .update(new: new, existing: existing):
81-
statement = try getUpdateExpression(tableName: self.targetTableName,
82-
newItem: new,
83-
existingItem: existing)
79+
try getUpdateExpression(tableName: self.targetTableName,
80+
newItem: new,
81+
existingItem: existing)
8482
case let .insert(new: new):
85-
statement = try getInsertExpression(tableName: self.targetTableName,
86-
newItem: new)
83+
try getInsertExpression(tableName: self.targetTableName,
84+
newItem: new)
8785
case let .deleteAtKey(key: key):
88-
statement = try getDeleteExpression(tableName: self.targetTableName,
89-
existingKey: key)
86+
try getDeleteExpression(tableName: self.targetTableName,
87+
existingKey: key)
9088
case let .deleteItem(existing: existing):
91-
statement = try getDeleteExpression(tableName: self.targetTableName,
92-
existingItem: existing)
89+
try getDeleteExpression(tableName: self.targetTableName,
90+
existingItem: existing)
9391
}
9492

9593
return statement
9694
}
9795

9896
internal func entryToStatement(
99-
_ entry: TransactionConstraintEntry<some Any, some Any, some Any>) throws -> String
97+
_ entry: TransactionConstraintEntry<some Any, some Sendable, some Any>) throws -> String
10098
{
101-
let statement: String
102-
switch entry {
99+
let statement: String = switch entry {
103100
case let .required(existing: existing):
104-
statement = getExistsExpression(tableName: self.targetTableName,
105-
existingItem: existing)
101+
getExistsExpression(tableName: self.targetTableName,
102+
existingItem: existing)
106103
}
107104

108105
return statement
@@ -236,11 +233,10 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
236233

237234
var isTransactionConflict = false
238235
let reasons = try zip(cancellationReasons, keys).compactMap { cancellationReason, entryKey -> DynamoDBTableError? in
239-
let key: CompositePrimaryKey<AttributesType>?
240-
if let item = cancellationReason.item {
241-
key = try DynamoDBDecoder().decode(.m(item))
236+
let key: CompositePrimaryKey<AttributesType>? = if let item = cancellationReason.item {
237+
try DynamoDBDecoder().decode(.m(item))
242238
} else {
243-
key = nil
239+
nil
244240
}
245241

246242
let partitionKey = key?.partitionKey ?? entryKey.partitionKey
@@ -339,11 +335,10 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
339335

340336
var isTransactionConflict = false
341337
let reasons = try zip(cancellationReasons, inputKeys).compactMap { cancellationReason, entryKey -> DynamoDBTableError? in
342-
let key: CompositePrimaryKey<AttributesType>?
343-
if let item = cancellationReason.item {
344-
key = try DynamoDBDecoder().decode(.m(item))
338+
let key: CompositePrimaryKey<AttributesType>? = if let item = cancellationReason.item {
339+
try DynamoDBDecoder().decode(.m(item))
345340
} else {
346-
key = nil
341+
nil
347342
}
348343

349344
let partitionKey = key?.partitionKey ?? entryKey.partitionKey
@@ -469,21 +464,20 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
469464
}
470465

471466
let statements = try entries.map { entry -> DynamoDBClientTypes.BatchStatementRequest in
472-
let statement: String
473-
switch entry {
467+
let statement: String = switch entry {
474468
case let .update(new: new, existing: existing):
475-
statement = try getUpdateExpression(tableName: self.targetTableName,
476-
newItem: new,
477-
existingItem: existing)
469+
try getUpdateExpression(tableName: self.targetTableName,
470+
newItem: new,
471+
existingItem: existing)
478472
case let .insert(new: new):
479-
statement = try getInsertExpression(tableName: self.targetTableName,
480-
newItem: new)
473+
try getInsertExpression(tableName: self.targetTableName,
474+
newItem: new)
481475
case let .deleteAtKey(key: key):
482-
statement = try getDeleteExpression(tableName: self.targetTableName,
483-
existingKey: key)
476+
try getDeleteExpression(tableName: self.targetTableName,
477+
existingKey: key)
484478
case let .deleteItem(existing: existing):
485-
statement = try getDeleteExpression(tableName: self.targetTableName,
486-
existingItem: existing)
479+
try getDeleteExpression(tableName: self.targetTableName,
480+
existingItem: existing)
487481
}
488482

489483
return DynamoDBClientTypes.BatchStatementRequest(consistentRead: self.tableConfiguration.consistentRead, statement: statement)
@@ -515,15 +509,13 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
515509
}
516510
}
517511

518-
func bulkWriteWithFallback<AttributesType, ItemType, TimeToLiveAttributesType>(
512+
func bulkWriteWithFallback<AttributesType, ItemType: Sendable, TimeToLiveAttributesType>(
519513
_ entries: [WriteEntry<AttributesType, ItemType, TimeToLiveAttributesType>]) async throws
520514
{
521-
// fall back to singel operation if the write entry exceeds the statement length limitation
522-
var bulkWriteEntries: [WriteEntry<AttributesType, ItemType, TimeToLiveAttributesType>] = []
523-
let errors: [DynamoDBTableError] = try await entries.concurrentCompactMap { entry -> DynamoDBTableError? in
515+
// fall back to single operation if the write entry exceeds the statement length limitation
516+
let results: [Result<WriteEntry<AttributesType, ItemType, TimeToLiveAttributesType>, DynamoDBTableError>] = try await entries.concurrentMap { entry in
524517
do {
525518
try self.validateEntry(entry: entry)
526-
bulkWriteEntries.append(entry)
527519
} catch DynamoDBTableError.statementLengthExceeded {
528520
do {
529521
switch entry {
@@ -537,11 +529,22 @@ public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
537529
try await self.deleteItem(existingItem: existing)
538530
}
539531
} catch let error as DynamoDBTableError {
540-
return error
532+
return .failure(error)
541533
}
542534
}
543535

544-
return nil
536+
return .success(entry)
537+
}
538+
539+
var bulkWriteEntries: [WriteEntry<AttributesType, ItemType, TimeToLiveAttributesType>] = []
540+
var errors: [DynamoDBTableError] = []
541+
for result in results {
542+
switch result {
543+
case let .success(entry):
544+
bulkWriteEntries.append(entry)
545+
case let .failure(error):
546+
errors.append(error)
547+
}
545548
}
546549

547550
do {

0 commit comments

Comments
 (0)