Skip to content

Commit c45609a

Browse files
Activity Execution Info to support checking if heartbeat details from previous attempt are available (#41)
### Motivation Currently, we offer `heartbeatDetails(as:)` method to retrieve the activity details of an activity execution. If it's the first attempt, this method fails with an ArgumentError `Mismatched number of values and payloads` as there are no heartbeat details available. The user has no way to differentiate between no heartbeat details being available or that they did indeed supple the wrong amount of payload types. ### Modifications We have `heartbeatDetails(as:)` return an optional tuple instead, so users can check if there are heartbeat details available from previous activity attempts. ### Test Plan Augmented existing unit tests to check activity details to be present or not.
1 parent bc35ca5 commit c45609a

File tree

2 files changed

+47
-32
lines changed

2 files changed

+47
-32
lines changed

Sources/Temporal/Worker/Activities/ActivityExecutionContext.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,20 @@ public struct ActivityExecutionContext: Sendable {
191191
///
192192
/// - Parameter detailTypes: The types to convert the heartbeat details to, specified as
193193
/// variadic generic parameters.
194-
/// - Returns: A tuple containing the converted heartbeat details in the order specified.
194+
/// - Returns: A tuple containing the converted heartbeat details in the order specified. Returns `nil` if there are no heartbeat details available from a previous activity attempt.
195195
/// - Throws: Conversion errors if the number of types doesn't match the stored details or if
196196
/// deserialization fails.
197197
public func heartbeatDetails<each HeartbeatDetail: Sendable>(
198198
as detailTypes: repeat (each HeartbeatDetail).Type
199-
) async throws -> (repeat each HeartbeatDetail) {
200-
try await self.dataConverter.convertPayloads(
199+
) async throws -> (repeat each HeartbeatDetail)? {
200+
guard !self.heartbeatDetails.isEmpty else {
201+
return nil
202+
}
203+
204+
return try await self.dataConverter.convertPayloads(
201205
self.heartbeatDetails,
202206
as: repeat (each detailTypes).self
203-
)
207+
) as (repeat each HeartbeatDetail)
204208
}
205209
}
206210

Tests/TemporalTests/Worker/Activities/ActivityWorkerTests.swift

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -231,21 +231,25 @@ private struct BadHeartbeatAcivity: ActivityDefinition {
231231
}
232232
}
233233

234-
private struct ReadingHeartbeatAcivity: ActivityDefinition {
234+
private struct ReadingHeartbeatActivity: ActivityDefinition {
235235
struct HeartbeatDetails: Codable, Hashable {
236236
var string: String
237237
var data: Data
238238
}
239239
typealias Input = Void
240-
typealias Output = HeartbeatDetails
240+
typealias Output = HeartbeatDetails?
241241

242-
static let name: String? = "ReadingHeartbeatAcivity"
242+
static let name: String? = "ReadingHeartbeatActivity"
243243

244-
func run(input: Void) async throws -> HeartbeatDetails {
245-
let (string, data) = try await ActivityExecutionContext.current!.info.heartbeatDetails(
246-
as: String.self,
247-
Data.self
248-
)
244+
func run(input: Void) async throws -> HeartbeatDetails? {
245+
guard
246+
let (string, data) = try await ActivityExecutionContext.current!.info.heartbeatDetails(
247+
as: String.self,
248+
Data.self
249+
)
250+
else {
251+
return nil
252+
}
249253

250254
return .init(string: string, data: data)
251255
}
@@ -748,9 +752,9 @@ struct ActivityWorkerTests {
748752
}
749753
}
750754

751-
@Test
752-
static func readingHeartbeat() async throws {
753-
let test = ActivityWorkerTests(activities: [ReadingHeartbeatAcivity()])
755+
@Test("Read Heartbeat Details", arguments: [true, false])
756+
static func readingHeartbeat(heartbeatDetailsAvailable: Bool) async throws {
757+
let test = ActivityWorkerTests(activities: [ReadingHeartbeatActivity()])
754758

755759
try await withThrowingTaskGroup(of: Void.self) { group in
756760
group.addTask {
@@ -760,40 +764,47 @@ struct ActivityWorkerTests {
760764
test.bridgeWorker.activityTaskContinuation.yield(
761765
.with {
762766
$0.taskToken = Data([1])
763-
$0.start.activityType = "ReadingHeartbeatAcivity"
767+
$0.start.activityType = "ReadingHeartbeatActivity"
764768
$0.start.activityID = "ActivityID1"
765769
$0.start.attempt = 1
766770
$0.start.workflowType = "WorkflowType"
767771
$0.start.workflowExecution = .with {
768772
$0.runID = "RunID"
769773
$0.workflowID = "WorkflowID1"
770774
}
771-
$0.start.heartbeatDetails = [
772-
.with {
773-
$0.data = Data(#""Foo""#.utf8)
774-
$0.metadata = ["encoding": Data("json/plain".utf8)]
775-
},
776-
.with {
777-
$0.data = Data([1, 2, 3])
778-
$0.metadata = ["encoding": Data("binary/plain".utf8)]
779-
},
780-
]
775+
if heartbeatDetailsAvailable {
776+
$0.start.heartbeatDetails = [
777+
.with {
778+
$0.data = Data(#""Foo""#.utf8)
779+
$0.metadata = ["encoding": Data("json/plain".utf8)]
780+
},
781+
.with {
782+
$0.data = Data([1, 2, 3])
783+
$0.metadata = ["encoding": Data("binary/plain".utf8)]
784+
},
785+
]
786+
}
781787
}
782788
)
783789

784790
var activityTaskCompletionIterator = test.bridgeWorker.activityTaskCompletionStream.makeAsyncIterator()
785791
let completion = try await activityTaskCompletionIterator.next()
786792
#expect(completion?.taskToken == Data([1]))
787793
let jsonDecoder = JSONDecoder()
788-
let expectedHeartbeatDetails = ReadingHeartbeatAcivity.HeartbeatDetails(
789-
string: "Foo",
790-
data: Data([1, 2, 3])
791-
)
792794
let heartbeatDetails = try jsonDecoder.decode(
793-
ReadingHeartbeatAcivity.HeartbeatDetails.self,
795+
(ReadingHeartbeatActivity.HeartbeatDetails?).self,
794796
from: completion!.result.completed.result.data
795797
)
796-
#expect(heartbeatDetails == expectedHeartbeatDetails)
798+
799+
if heartbeatDetailsAvailable {
800+
let expectedHeartbeatDetails = ReadingHeartbeatActivity.HeartbeatDetails(
801+
string: "Foo",
802+
data: Data([1, 2, 3])
803+
)
804+
#expect(heartbeatDetails == expectedHeartbeatDetails)
805+
} else {
806+
#expect(heartbeatDetails == nil)
807+
}
797808
group.cancelAll()
798809
}
799810
}

0 commit comments

Comments
 (0)