Skip to content
Draft
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

*.orig
*.app
.history

Instruments/ActorInstruments/ActorInstruments.xcodeproj/xcuserdata
Instruments/ActorInstruments/build/
Expand Down
9 changes: 6 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ var targets: [PackageDescription.Target] = [
.product(name: "NIOSSL", package: "swift-nio-ssl"),
.product(name: "NIOExtras", package: "swift-nio-extras"),
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
.product(name: "Logging", package: "swift-log"),
.product(name: "Metrics", package: "swift-metrics"),
.product(name: "ServiceDiscovery", package: "swift-service-discovery"),
.product(name: "Backtrace", package: "swift-backtrace"),
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
// Observability
.product(name: "Logging", package: "swift-log"),
.product(name: "Metrics", package: "swift-metrics"),
.product(name: "Tracing", package: "swift-distributed-tracing"),
]
),

Expand Down Expand Up @@ -182,7 +184,8 @@ var dependencies: [Package.Dependency] = [
.package(url: "https://github.com/apple/swift-collections", from: "1.0.1"),

// ~~~ Observability ~~~
.package(url: "https://github.com/apple/swift-log", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-log", from: "1.4.0"),
.package(url: "https://github.com/apple/swift-distributed-tracing", from: "0.3.0"),
// swift-metrics 1.x and 2.x are almost API compatible, so most clients should use
.package(url: "https://github.com/apple/swift-metrics", "1.0.0" ..< "3.0.0"),
.package(url: "https://github.com/apple/swift-service-discovery", from: "1.0.0"),
Expand Down
34 changes: 33 additions & 1 deletion Samples/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var targets: [PackageDescription.Target] = [
name: "SampleDiningPhilosophers",
dependencies: [
.product(name: "DistributedCluster", package: "swift-distributed-actors"),
"_PrettyLogHandler",
],
path: "Sources/SampleDiningPhilosophers",
exclude: [
Expand All @@ -29,6 +30,33 @@ var targets: [PackageDescription.Target] = [
]
),

.executableTarget(
name: "SampleClusterTracing",
dependencies: [
.product(name: "DistributedCluster", package: "swift-distributed-actors"),
.product(name: "OpenTelemetry", package: "opentelemetry-swift"),
.product(name: "OtlpGRPCSpanExporting", package: "opentelemetry-swift"),
"_PrettyLogHandler",
]
),

.executableTarget(
name: "SampleClusterBuilds",
dependencies: [
.product(name: "DistributedCluster", package: "swift-distributed-actors"),
.product(name: "OpenTelemetry", package: "opentelemetry-swift"),
.product(name: "OtlpGRPCSpanExporting", package: "opentelemetry-swift"),
"_PrettyLogHandler",
]
),

.target(
name: "_PrettyLogHandler",
dependencies: [
.product(name: "DistributedCluster", package: "swift-distributed-actors"),
]
),

/* --- tests --- */

// no-tests placeholder project to not have `swift test` fail on Samples/
Expand All @@ -45,6 +73,7 @@ var dependencies: [Package.Dependency] = [
.package(name: "swift-distributed-actors", path: "../"),

// ~~~~~~~ only for samples ~~~~~~~
.package(url: "https://github.com/slashmo/opentelemetry-swift", from: "0.3.0"),
]

let package = Package(
Expand All @@ -58,11 +87,14 @@ let package = Package(
],
products: [
/* --- samples --- */

.executable(
name: "SampleDiningPhilosophers",
targets: ["SampleDiningPhilosophers"]
),
.executable(
name: "SampleClusterTracing",
targets: ["SampleClusterTracing"]
),
],

dependencies: dependencies,
Expand Down
69 changes: 69 additions & 0 deletions Samples/Sources/SampleClusterBuilds/ClusterBuilds.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Distributed Actors open source project
//
// Copyright (c) 2018-2022 Apple Inc. and the Swift Distributed Actors project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import _PrettyLogHandler
import Distributed
import DistributedCluster
import Logging
import NIO
import OpenTelemetry
import OtlpGRPCSpanExporting
import Tracing

struct ClusterBuilds {
let system: ClusterSystem
var workers: [ActorID: BuildWorker] = [:]

init(name: String, port: Int) async {
self.system = await ClusterSystem(name) { settings in
settings.bindPort = port

settings.plugins.install(plugin: ClusterSingletonPlugin())

// We are purposefully making allowing long calls:
settings.remoteCall.defaultTimeout = .seconds(20)

// Try joining this seed node automatically; once we have joined at least once node, we'll learn about others.
settings.discovery = ServiceDiscoverySettings(static: [
Main.Config.seedEndpoint,
])
}
self.system.cluster.join(endpoint: Main.Config.seedEndpoint)
}

mutating func run(tasks: Int) async throws {
var singletonSettings = ClusterSingletonSettings()
singletonSettings.allocationStrategy = .byLeadership

// Pretend we have some work to do:
let buildTasks: [BuildTask] = (0 ..< tasks).map { _ in BuildTask() }

// anyone can host the singleton, but by default, it'll be on the build leader (7330) various strategies are possible.
try await system.singleton.host(name: BuildLeader.singletonName) { actorSystem in
await BuildLeader(buildTasks: buildTasks, actorSystem: actorSystem)
}

// all nodes, except the build-leader node contain a few workers:
if self.system.isBuildWorker {
for _ in 0 ..< Main.Config.workersPerNode {
await makeWorker()
}
}
}

private mutating func makeWorker() async {
let worker = await BuildWorker(actorSystem: self.system)
self.workers[worker.id] = worker
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Distributed Actors open source project
//
// Copyright (c) 2018-2022 Apple Inc. and the Swift Distributed Actors project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import _PrettyLogHandler
import Distributed
import DistributedCluster
import Logging
import NIO
import OpenTelemetry
import OtlpGRPCSpanExporting
import Tracing

// Sleep, with adding a little bit of noise (additional delay) to the duration.
func noisySleep(for duration: ContinuousClock.Duration) async {
var duration = duration + .milliseconds(Int.random(in: 200 ..< 500))
try? await Task.sleep(until: ContinuousClock.now + duration, clock: .continuous)
}
33 changes: 33 additions & 0 deletions Samples/Sources/SampleClusterBuilds/Convenience/Roles.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Distributed Actors open source project
//
// Copyright (c) 2018-2022 Apple Inc. and the Swift Distributed Actors project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import _PrettyLogHandler
import Distributed
import DistributedCluster
import Logging
import NIO
import OpenTelemetry
import OtlpGRPCSpanExporting
import Tracing

// TODO: we want to support "member roles" in membership, this would be then a built in query, and not hardcoded by port either.
extension ClusterSystem {
var isBuildLeader: Bool {
self.cluster.endpoint.port == 7330
}

var isBuildWorker: Bool {
self.cluster.endpoint.port > 7330
}
}
28 changes: 28 additions & 0 deletions Samples/Sources/SampleClusterBuilds/Convenience/Weak.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Distributed Actors open source project
//
// Copyright (c) 2018-2022 Apple Inc. and the Swift Distributed Actors project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Distributed
import DistributedCluster

final class Weak<Act: DistributedActor> {
weak var actor: Act?

init(_ actor: Act) {
self.actor = actor
}

init(idForRemoval id: ClusterSystem.ActorID) {
self.actor = nil
}
}
18 changes: 18 additions & 0 deletions Samples/Sources/SampleClusterBuilds/DefaultActorSystem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Distributed Actors open source project
//
// Copyright (c) 2018-2022 Apple Inc. and the Swift Distributed Actors project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Distributed
import DistributedCluster

typealias DefaultDistributedActorSystem = ClusterSystem
55 changes: 55 additions & 0 deletions Samples/Sources/SampleClusterBuilds/Model.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Distributed Actors open source project
//
// Copyright (c) 2018-2022 Apple Inc. and the Swift Distributed Actors project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of Swift Distributed Actors project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import _PrettyLogHandler
import Distributed
import DistributedCluster
import struct Foundation.UUID
import Logging
import NIO
import OpenTelemetry
import OtlpGRPCSpanExporting
import Tracing

struct BuildTask: Sendable, Codable, Hashable {
let id: BuildTaskID

// this would have some state about "what to build"

init() {
self.id = .init()
}

init(id: BuildTaskID) {
self.id = id
}
}

/// A trivial "build result" representation, not carrying additional information, just for demonstration purposes.
enum BuildResult: Sendable, Codable, Equatable {
case successful
case failed
case rejected
}

struct BuildTaskID: Sendable, Codable, Hashable, CustomStringConvertible {
let id: UUID
init() {
self.id = UUID()
}

var description: String {
"\(id)"
}
}
Loading