Skip to content
Open
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
36 changes: 17 additions & 19 deletions Sources/BreezeDynamoDBService/BreezeDynamoDBService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ import Logging
/// Defines the interface for a Breeze DynamoDB service.
///
/// Provides methods to access the database manager and to gracefully shutdown the service.
public protocol BreezeDynamoDBServing: Actor {
func dbManager() async -> BreezeDynamoDBManaging
func gracefulShutdown() throws
public protocol BreezeDynamoDBServing: Service {
var dbManager: BreezeDynamoDBManaging { get }
func onGracefulShutdown() async throws
func syncShutdown() throws
}

/// Provides methods to access the DynamoDB database manager and to gracefully shutdown the service.
public actor BreezeDynamoDBService: BreezeDynamoDBServing {
public struct BreezeDynamoDBService: BreezeDynamoDBServing {

private let dbManager: BreezeDynamoDBManaging
public let dbManager: BreezeDynamoDBManaging
private let logger: Logger
private let awsClient: AWSClient
private let httpClient: HTTPClient
Expand All @@ -46,7 +47,7 @@ public actor BreezeDynamoDBService: BreezeDynamoDBServing {
httpConfig: BreezeHTTPClientConfig,
logger: Logger,
DBManagingType: BreezeDynamoDBManaging.Type = BreezeDynamoDBManager.self
) async {
) {
logger.info("Init DynamoDBService with config...")
logger.info("region: \(config.region)")
logger.info("tableName: \(config.tableName)")
Expand Down Expand Up @@ -76,10 +77,9 @@ public actor BreezeDynamoDBService: BreezeDynamoDBServing {
logger.info("DBManager is ready.")
}

/// Returns the BreezeDynamoDBManaging instance.
public func dbManager() async -> BreezeDynamoDBManaging {
logger.info("Starting DynamoDBService...")
return self.dbManager
public func run() async throws {
try await gracefulShutdown()
try await onGracefulShutdown()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you shutting down immediately at run() ? Does it work ? Why ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function try await gracefulShutdown() waits until graceful shutdown is triggered as documented in ServiceLifecycle

https://swiftpackageindex.com/swift-server/swift-service-lifecycle/main/documentation/servicelifecycle/gracefulshutdown()

}

/// Gracefully shutdown the service and its components.
Expand All @@ -89,21 +89,19 @@ public actor BreezeDynamoDBService: BreezeDynamoDBServing {
/// It also logs the shutdown process.
/// This method is idempotent;
/// - Important: This method must be called at leat once to ensure that resources are released properly. If the method is not called, it will lead to a crash.
public func gracefulShutdown() throws {
guard !isShutdown else { return }
isShutdown = true
public func onGracefulShutdown() async throws {
logger.info("Stopping DynamoDBService...")
try awsClient.syncShutdown()
try await awsClient.shutdown()
logger.info("DynamoDBService is stopped.")
logger.info("Stopping HTTPClient...")
try httpClient.syncShutdown()
try await httpClient.shutdown()
logger.info("HTTPClient is stopped.")
}

deinit {
guard !isShutdown else { return }
try? awsClient.syncShutdown()
try? httpClient.syncShutdown()
/// Sync shutdown
public func syncShutdown() throws {
try awsClient.syncShutdown()
try httpClient.syncShutdown()
}
}

23 changes: 14 additions & 9 deletions Sources/BreezeLambdaAPI/BreezeLambdaAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public actor BreezeLambdaAPI<T: BreezeCodable>: Service {
let timeout: TimeAmount
private let serviceGroup: ServiceGroup
private let apiConfig: any APIConfiguring
private let dynamoDBService: BreezeDynamoDBService

/// Initializes the BreezeLambdaAPI with the provided API configuration.
/// - Parameter apiConfig: An object conforming to `APIConfiguring` that provides the necessary configuration for the Breeze API.
Expand All @@ -63,19 +64,18 @@ public actor BreezeLambdaAPI<T: BreezeCodable>: Service {
logger: logger
)
let operation = try apiConfig.operation()
let dynamoDBService = await BreezeDynamoDBService(
self.dynamoDBService = BreezeDynamoDBService(
config: config,
httpConfig: httpConfig,
logger: logger
)
let breezeLambdaService = BreezeLambdaService<T>(
dynamoDBService: dynamoDBService,
operation: operation,
logger: logger
)
let dbManager = dynamoDBService.dbManager
let breezeApi = BreezeLambdaHandler<T>(dbManager: dbManager, operation: operation)
let runtime = LambdaRuntime(body: breezeApi.handle)
self.serviceGroup = ServiceGroup(
services: [breezeLambdaService],
gracefulShutdownSignals: [.sigterm, .sigint],
services: [runtime, dynamoDBService],
gracefulShutdownSignals: [.sigint],
cancellationSignals: [.sigterm],
logger: logger
)
} catch {
Expand All @@ -90,7 +90,12 @@ public actor BreezeLambdaAPI<T: BreezeCodable>: Service {
/// The internal ServiceGroup will handle the lifecycle of the BreezeLambdaAPI, including starting and stopping the service gracefully.
public func run() async throws {
logger.info("Starting BreezeLambdaAPI...")
try await serviceGroup.run()
do {
try await serviceGroup.run()
} catch {
try dynamoDBService.syncShutdown()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the life cycle group must do that. You should not shutdown() or cancel() a service part of the group

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it does unless there is a failure condition. Without this BreezeLambdaAPITests will crash.
I'll check if there is a better way.

throw error
}
logger.info("BreezeLambdaAPI is stopped successfully")
}
}
126 changes: 0 additions & 126 deletions Sources/BreezeLambdaAPI/BreezeLambdaService.swift

This file was deleted.

19 changes: 11 additions & 8 deletions Tests/BreezeDynamoDBServiceTests/BreezeDynamoDBServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@ struct BreezeDynamoDBServiceTests {
@Test
func testInitPrepareBreezeDynamoDBManager() async throws {
let sut = await makeBreezeDynamoDBConfig()
let manager = await sut.dbManager()
let manager =
sut.dbManager
#expect(manager is BreezeDynamoDBManager, "Expected BreezeDynamoDBManager instance")
try await sut.gracefulShutdown()
try await sut.onGracefulShutdown()
}

@Test
func testGracefulShutdownCanBeCalledMultipleTimes() async throws {
let sut = await makeBreezeDynamoDBConfig()
try await sut.gracefulShutdown()
try await sut.gracefulShutdown()
try await sut.onGracefulShutdown()
await #expect(throws: Error.self) {
try await sut.onGracefulShutdown()
}
}

@Test
Expand All @@ -44,15 +47,15 @@ struct BreezeDynamoDBServiceTests {
)
let logger = Logger(label: "BreezeDynamoDBServiceTests")
let httpConfig = BreezeHTTPClientConfig(timeout: .seconds(10), logger: logger)
let sut = await BreezeDynamoDBService(
let sut = BreezeDynamoDBService(
config: config,
httpConfig: httpConfig,
logger: logger,
DBManagingType: BreezeDynamoDBManagerMock.self
)
let manager = await sut.dbManager()
let manager = sut.dbManager
#expect(manager is BreezeDynamoDBManagerMock, "Expected BreezeDynamoDBManager instance")
try await sut.gracefulShutdown()
try await sut.onGracefulShutdown()
}

private func makeBreezeDynamoDBConfig() async -> BreezeDynamoDBService {
Expand All @@ -63,7 +66,7 @@ struct BreezeDynamoDBServiceTests {
)
let logger = Logger(label: "BreezeDynamoDBServiceTests")
let httpConfig = BreezeHTTPClientConfig(timeout: .seconds(10), logger: logger)
return await BreezeDynamoDBService(
return BreezeDynamoDBService(
config: config,
httpConfig: httpConfig,
logger: logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ struct APIConfiguration: APIConfiguring {
}

@Suite
struct BreezeLambdaAPIServiceTests {
struct BreezeLambdaAPITests {

let logger = Logger(label: "BreezeHTTPClientServiceTests")

@Test
func test_breezeLambdaAPIService_whenValidEnvironment() async throws {
func test_breezeLambdaAPI_whenValidEnvironment() async throws {
try await testGracefulShutdown { gracefulShutdownTestTrigger in

let (gracefulStream, continuation) = AsyncStream<Void>.makeStream()
Expand All @@ -49,8 +49,12 @@ struct BreezeLambdaAPIServiceTests {
}
group.addTask {
try await withGracefulShutdownHandler {
try await sut.run()
print("BreezeLambdaAPIService started successfully")
do {
try await sut.run()
print("BreezeLambdaAPIService started successfully")
} catch {

}
} onGracefulShutdown: {
logger.info("On Graceful Shutdown")
continuation.yield()
Expand All @@ -66,7 +70,7 @@ struct BreezeLambdaAPIServiceTests {
}

@Test
func test_breezeLambdaAPIService_whenInvalidEnvironment() async throws {
func test_breezeLambdaAPI_whenInvalidEnvironment() async throws {
await #expect(throws: BreezeLambdaAPIError.self) {
try await testGracefulShutdown { gracefulShutdownTestTrigger in
try await withThrowingTaskGroup(of: Void.self) { group in
Expand Down