Skip to content

Commit ceaeb30

Browse files
authored
Hummingbird2 updates (#9043)
* Use one connection for mulitple queries * Use prepared statements * Reduce connection amount to 100
1 parent 48b96fd commit ceaeb30

File tree

3 files changed

+70
-34
lines changed

3 files changed

+70
-34
lines changed

frameworks/Swift/hummingbird2/src-postgres/Sources/server/Controllers/FortunesController.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,27 @@ final class FortunesController: Sendable {
3535
/// is delivered to the client using a server-side HTML template. The message
3636
/// text must be considered untrusted and properly escaped and the UTF-8 fortune messages must be rendered properly.
3737
@Sendable func fortunes(request: Request, context: Context) async throws -> HTML {
38-
let rows = try await self.postgresClient.query("SELECT id, message FROM Fortune")
38+
let rows = try await self.postgresClient.execute(SelectFortuneStatement())
3939
var fortunes: [Fortune] = []
40-
for try await (id, message) in rows.decode((Int32, String).self, context: .default) {
41-
fortunes.append(.init(id: id, message: message))
40+
for try await fortune in rows {
41+
fortunes.append(.init(id: fortune.0, message: fortune.1))
4242
}
4343

4444
fortunes.append(.init(id: 0, message: "Additional fortune added at request time."))
4545
let sortedFortunes = fortunes.sorted { $0.message < $1.message }
4646
return HTML(html: self.template.render(sortedFortunes) )
4747

4848
}
49+
50+
struct SelectFortuneStatement: PostgresPreparedStatement {
51+
typealias Row = (Int32, String)
52+
53+
static var sql = "SELECT id, message FROM Fortune"
54+
55+
func makeBindings() throws -> PostgresNIO.PostgresBindings {
56+
return .init()
57+
}
58+
59+
func decodeRow(_ row: PostgresNIO.PostgresRow) throws -> Row { try row.decode(Row.self) }
60+
}
4961
}

frameworks/Swift/hummingbird2/src-postgres/Sources/server/Controllers/WorldController.swift

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ struct WorldController {
1616
/// simple database table. That row is then serialized as a JSON response.
1717
@Sendable func single(request: Request, context: Context) async throws -> World {
1818
let id = Int32.random(in: 1...10_000)
19-
let rows = try await self.postgresClient.query("SELECT id, randomnumber FROM World WHERE id = \(id)")
20-
for try await (id, randomNumber) in rows.decode((Int32, Int32).self, context: .default) {
21-
return World(id: id, randomNumber: randomNumber)
19+
let rows = try await self.postgresClient.execute(SelectWorldStatement(id: id))
20+
guard let row = try await rows.first(where: {_ in true }) else {
21+
throw HTTPError(.notFound)
2222
}
23-
throw HTTPError(.notFound)
23+
return World(id: row.0, randomNumber: row.1)
2424
}
2525

2626
/// In this test, each request is processed by fetching multiple rows from a
@@ -29,21 +29,16 @@ struct WorldController {
2929
/// All tests are run at 512 concurrency.
3030
@Sendable func multiple(request: Request, context: Context) async throws -> [World] {
3131
let queries = (request.uri.queryParameters.get("queries", as: Int.self) ?? 1).bound(1, 500)
32-
return try await withThrowingTaskGroup(of: World.self) { group in
32+
return try await self.postgresClient.withConnection { conn in
33+
var result: [World] = .init()
34+
result.reserveCapacity(queries)
3335
for _ in 0..<queries {
34-
group.addTask {
35-
let id = Int32.random(in: 1...10_000)
36-
let rows = try await self.postgresClient.query("SELECT id, randomnumber FROM World WHERE id = \(id)")
37-
for try await (id, randomNumber) in rows.decode((Int32, Int32).self, context: .default) {
38-
return World(id: id, randomNumber: randomNumber)
39-
}
36+
let id = Int32.random(in: 1...10_000)
37+
let rows = try await conn.execute(SelectWorldStatement(id: id), logger: context.logger)
38+
guard let row = try await rows.first(where: {_ in true }) else {
4039
throw HTTPError(.notFound)
4140
}
42-
}
43-
var result: [World] = .init()
44-
result.reserveCapacity(queries)
45-
for try await world in group {
46-
result.append(world)
41+
result.append( World(id: row.0, randomNumber: row.1))
4742
}
4843
return result
4944
}
@@ -59,25 +54,54 @@ struct WorldController {
5954
/// query to fetch the object. All tests are run at 512 concurrency.
6055
@Sendable func updates(request: Request, context: Context) async throws -> [World] {
6156
let queries = (request.uri.queryParameters.get("queries", as: Int.self) ?? 1).bound(1, 500)
62-
return try await withThrowingTaskGroup(of: World.self) { group in
57+
return try await self.postgresClient.withConnection { conn in
58+
var result: [World] = .init()
59+
result.reserveCapacity(queries)
6360
for _ in 0..<queries {
64-
group.addTask {
65-
let id = Int32.random(in: 1...10_000)
66-
let rows = try await self.postgresClient.query("SELECT id FROM World WHERE id = \(id)")
67-
for try await (id) in rows.decode((Int32).self, context: .default) {
68-
let randomNumber = Int32.random(in: 1...10_000)
69-
try await self.postgresClient.query("UPDATE World SET randomnumber = \(randomNumber) WHERE id = \(id)")
70-
return World(id: id, randomNumber: randomNumber)
71-
}
61+
let id = Int32.random(in: 1...10_000)
62+
let rows = try await conn.execute(SelectWorldStatement(id: id), logger: context.logger)
63+
guard let row = try await rows.first(where: {_ in true }) else {
7264
throw HTTPError(.notFound)
7365
}
74-
}
75-
var result: [World] = .init()
76-
result.reserveCapacity(queries)
77-
for try await world in group {
78-
result.append(world)
66+
let randomNumber = Int32.random(in: 1...10_000)
67+
_ = try await conn.execute(UpdateWorldStatement(id: id, randomNumber: randomNumber), logger: context.logger)
68+
result.append(World(id: row.0, randomNumber: randomNumber))
7969
}
8070
return result
8171
}
8272
}
73+
74+
struct SelectWorldStatement: PostgresPreparedStatement {
75+
typealias Row = (Int32, Int32)
76+
77+
let id: Int32
78+
79+
static var sql = "SELECT id, randomnumber FROM World WHERE id = $1"
80+
81+
func makeBindings() throws -> PostgresNIO.PostgresBindings {
82+
var bindings = PostgresNIO.PostgresBindings(capacity: 1)
83+
bindings.append(.init(int32: self.id))
84+
return bindings
85+
}
86+
87+
func decodeRow(_ row: PostgresNIO.PostgresRow) throws -> Row { try row.decode(Row.self) }
88+
}
89+
90+
struct UpdateWorldStatement: PostgresPreparedStatement {
91+
typealias Row = Int32
92+
93+
let id: Int32
94+
let randomNumber: Int32
95+
96+
static var sql = "UPDATE World SET randomnumber = $2 WHERE id = $1"
97+
98+
func makeBindings() throws -> PostgresNIO.PostgresBindings {
99+
var bindings = PostgresNIO.PostgresBindings(capacity: 2)
100+
bindings.append(.init(int32: self.id))
101+
bindings.append(.init(int32: self.randomNumber))
102+
return bindings
103+
}
104+
105+
func decodeRow(_ row: PostgresNIO.PostgresRow) throws -> Row { try row.decode(Row.self) }
106+
}
83107
}

frameworks/Swift/hummingbird2/src-postgres/Sources/server/main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func runApp() async throws {
3939
database: "hello_world",
4040
tls: .disable
4141
)
42-
postgresConfiguration.options.maximumConnections = 1900
42+
postgresConfiguration.options.maximumConnections = 100
4343
let postgresClient = PostgresClient(
4444
configuration: postgresConfiguration,
4545
eventLoopGroup: MultiThreadedEventLoopGroup.singleton

0 commit comments

Comments
 (0)