diff --git a/Package.swift b/Package.swift index 265e295..7085974 100644 --- a/Package.swift +++ b/Package.swift @@ -15,8 +15,8 @@ let package = Package( // https://github.com/Joannis/SMTPKitten <- No updates since 0ct 2020 // https://github.com/Joannis/VaporSMTPKit <- No updates since 0ct. 2020, uses SMTPKitten .package(url: "https://github.com/Kitura/Swift-SMTP", from: "6.0.0"), - .package(url: "https://github.com/christophhagen/Clairvoyant", from: "0.9.0"), - .package(url: "https://github.com/christophhagen/ClairvoyantVapor", from: "0.2.0"), + .package(url: "https://github.com/christophhagen/Clairvoyant", from: "0.11.2"), + .package(url: "https://github.com/christophhagen/ClairvoyantVapor", from: "0.5.0"), .package(url: "https://github.com/christophhagen/ClairvoyantBinaryCodable", from: "0.3.1"), ], targets: [ diff --git a/Sources/App/EventLoopScheduler.swift b/Sources/App/EventLoopScheduler.swift new file mode 100644 index 0000000..3997c05 --- /dev/null +++ b/Sources/App/EventLoopScheduler.swift @@ -0,0 +1,34 @@ +import Foundation +import Clairvoyant +import Vapor +import NIOCore + +final class EventLoopScheduler { + + private let backgroundGroup: EventLoopGroup + + init(numberOfThreads: Int = 2) { + backgroundGroup = MultiThreadedEventLoopGroup(numberOfThreads: numberOfThreads) + } + + func next() -> EventLoop { + backgroundGroup.next() + } + + func provider() -> NIOEventLoopGroupProvider { + return .shared(backgroundGroup) + } + + func shutdown() { + backgroundGroup.shutdownGracefully { _ in + + } + } +} + +extension EventLoopScheduler: AsyncScheduler { + + func schedule(asyncJob: @escaping @Sendable () async throws -> Void) { + _ = backgroundGroup.any().makeFutureWithTask(asyncJob) + } +} diff --git a/Sources/App/Management/SQLiteDatabase.swift b/Sources/App/Management/SQLiteDatabase.swift index 86aaacf..179376a 100644 --- a/Sources/App/Management/SQLiteDatabase.swift +++ b/Sources/App/Management/SQLiteDatabase.swift @@ -52,7 +52,7 @@ final class SQLiteDatabase { private let registeredPlayerCountMetric: Metric init(database: Database, mail: Configuration.EMail?) async throws { - self.registeredPlayerCountMetric = try await Metric( + self.registeredPlayerCountMetric = Metric( "schafkopf.players", name: "Number of users", description: "The total number of user accounts") diff --git a/Sources/App/Management/TableManagement.swift b/Sources/App/Management/TableManagement.swift index 75e1979..760c6ca 100644 --- a/Sources/App/Management/TableManagement.swift +++ b/Sources/App/Management/TableManagement.swift @@ -28,15 +28,15 @@ final class TableManagement { - Throws: Errors when the file could not be read */ init(database: Database) async throws { - self.tableCountMetric = try await .init( + self.tableCountMetric = .init( "schafkopf.tables", name: "Open tables", description: "The number of currently available tables") - self.playingPlayerCountMetric = try await .init( + self.playingPlayerCountMetric = .init( "schafkopf.playing", name: "Sitting players", description: "The number of players currently sitting at a table") - self.connectedPlayerCountMetric = try await .init( + self.connectedPlayerCountMetric = .init( "schafkopf.connected", name: "Connected players", description: "The number of players with a websocket connection to the server") diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 628501c..6d9e3e4 100644 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -7,6 +7,14 @@ import ClairvoyantVapor var server: SQLiteDatabase! private var provider: VaporMetricProvider! = nil +private let scheduler = EventLoopScheduler() +private var status: Metric! + +private func update(status newStatus: ServerStatus) { + scheduler.schedule { + _ = try? await status.update(newStatus) + } +} // configures your application public func configure(_ app: Application) async throws { @@ -18,12 +26,12 @@ public func configure(_ app: Application) async throws { logMetricId: "schafkopf.log") MetricObserver.standard = monitor - let status = try! await Metric( + status = Metric( "schafkopf.status", name: "Status", description: "The main status of the server") - _ = try? await status.update(.initializing) + update(status: .initializing) let configPath = URL(fileURLWithPath: app.directory.resourcesDirectory) .appendingPathComponent("config.json") @@ -32,8 +40,8 @@ public func configure(_ app: Application) async throws { do { configuration = try Configuration(loadFromUrl: configPath) } catch { - _ = try? await status.update(.initializationFailure) - monitor.log("Failed to read configuration: \(error)") + update(status: .initializationFailure) + await monitor.log("Failed to read configuration: \(error)") // Note: If configuration can't be loaded, then the server will run on the wrong port // and access to metrics is impossible, since no tokens are loaded return @@ -60,8 +68,8 @@ public func configure(_ app: Application) async throws { do { try await app.autoMigrate() } catch { - monitor.log("Failed to migrate database: \(error)") - _ = try? await status.update(.initializationFailure) + await monitor.log("Failed to migrate database: \(error)") + update(status: .initializationFailure) return } @@ -83,9 +91,10 @@ public func configure(_ app: Application) async throws { // Expose metrics provider = .init(observer: monitor, accessManager: configuration.monitoringTokens) + provider.asyncScheduler = scheduler provider.registerRoutes(app) - _ = try? await status.update(.nominal) + update(status: .nominal) } func log(_ message: String) { @@ -93,5 +102,7 @@ func log(_ message: String) { print(message) return } - observer.log(message) + scheduler.schedule { + await observer.log(message) + } }