External Storage Integration: Activity worker, client - #3020
Conversation
| public ListWorkflowExecutionsResponse listWorkflowExecutions( | ||
| ListWorkflowExecutionsRequest listRequest) { | ||
| return next.listWorkflowExecutions(listRequest); | ||
| } |
There was a problem hiding this comment.
Addressed the previous concern of eagerly fetching external payloads #2978 (comment). They are now loaded lazily.
37ae3c7 to
7069249
Compare
|
|
||
| try { | ||
| sendReply(taskToken, result, metricsScope); | ||
| sendReply(taskToken, result, metricsScope, activityStorageTarget(pollResponse)); |
There was a problem hiding this comment.
Previous comment about pivoting this target based on standalone vs workflow activity has been addressed #2978 (comment)
activityStorageTarget delegates to storageTargetForActivityTask (which can be seen above)
460bfbf to
b3804da
Compare
7069249 to
550e4f2
Compare
b3804da to
ca09b50
Compare
ffafefa to
69a8a3a
Compare
fad5ed3 to
8d1399c
Compare
| * <p>This This is an internal class that is not exposed to users or workflow code. The intent is to | ||
| * use this data converter to consolidate extstore usage within the SDK. | ||
| */ | ||
| public final class ExternalStorageDataConverter implements DataConverter { |
There was a problem hiding this comment.
This is the new abstraction that replaces the previous External Storage client decorator. It wraps the data converter supplied to the WorkflowClient in RootWorkflowClientInvoker
| public RootWorkflowClientInvoker( | ||
| GenericWorkflowClient genericClient, | ||
| WorkflowClientOptions clientOptions, | ||
| WorkerFactoryRegistry workerFactoryRegistry, | ||
| @Nullable ExternalStorageRunner externalStorage) { | ||
| this.externalStorage = externalStorage; |
There was a problem hiding this comment.
This class inherits the external storage runner from WorkflowClientInternalImpl and then uses it to create the new ExternalStorageDataConverter
| Stream<HistoryEvent> streamHistory(WorkflowExecution execution) { | ||
| Preconditions.checkNotNull(execution, "execution is required"); | ||
|
|
||
| GetWorkflowExecutionHistoryIterator iterator = | ||
| new GetWorkflowExecutionHistoryIterator( | ||
| options.getNamespace(), execution, null, genericClient); | ||
| iterator.init(); |
There was a problem hiding this comment.
This no longer runs through external storage as it was previously being handled by the client decorator. The worker still uses extstore when replaying it's own history (see https://github.com/temporalio/sdk-java/pull/3017/changes#diff-4928a50c4af33d91cd6b57a121d20ed2594cd314e118beb01c259d5e7427728c).
Is this just used for offline replay? Do we need to use extstore here?
There was a problem hiding this comment.
I think we discussed offline we don't need to? At least that is what other SDKs did so we should be consistent
8d1399c to
0c7b308
Compare
|
Couple nits, generally looks good. Going to do an AI review as well. Would want Justin to look as well of course |
|
Codex identified these two additional issues At a quick glance they look legit, but please let me know. |
2d2d90e to
504467c
Compare
f527885 to
2cedf19
Compare
|
The first issue that codex identified led me to do a little refactoring. I believe all the feedback has been addressed here. |
…TestActivityEnvironment
…rator, address some feedback points.
…ly have one exposed path for getting the data converter.
ec3528f to
050c637
Compare
| } | ||
|
|
||
| private ActivityTaskHandler.Result handleActivity(ActivityTask task, Scope metricsScope) { | ||
| task = retrieveInboundPayloads(task); |
There was a problem hiding this comment.
If external storage throws, then there is no recovery. Exceptions here should proactively fail the activity task.
| sendReply(taskToken, result, metricsScope); | ||
| sendReply(taskToken, result, metricsScope, activityStorageTarget(pollResponse)); | ||
| } catch (ExternalStorageTaskFailure e) { | ||
| sendStorageFailure(taskToken, pollResponse, metricsScope, e.getCause()); |
There was a problem hiding this comment.
Should be consistent with the other PRs and send the storage failure without using external storage.
| .setWorkerVersion(options.workerVersionStamp()) | ||
| .build(); | ||
| .setWorkerVersion(options.workerVersionStamp()); | ||
| storeOutboundPayloads(completedBuilder, storageTarget); |
There was a problem hiding this comment.
This and all other call sites should recover and send activity task failure without external storage.
| * Offloads large heartbeat payloads aborting if the store call runs longer than the heartbeat | ||
| * interval or if the activity is cancelled. | ||
| */ | ||
| private void offloadHeartbeat(RecordActivityTaskHeartbeatRequest.Builder builder) { |
There was a problem hiding this comment.
I believe this runs under the heartbeat lock. And the cancellation below looks to be relying on the heartbeat interval, which has a default of 30 seconds. This is could also starve out other operations like getHeartbeatDetails as they queue up. Also, if too many activities get stuck on external storage during heart beating, it might exhaust the thread pool and activity cancels may not run.
| .setIdentity(identity) | ||
| .setNamespace(namespace) | ||
| .setRequestId(UUID.randomUUID().toString()) | ||
| .setHeader(HeaderUtils.toHeaderGrpc(input.getHeader(), null)); |
There was a problem hiding this comment.
Need to convert headers. This cannot be done with the DataConverter because they are already Payloads. Haven't looked for all instances but pointing out this one category.
| this.baseConverter = | ||
| externalStorage == null | ||
| ? clientOptions.getDataConverter() | ||
| : new ExternalStorageDataConverter(clientOptions.getDataConverter(), externalStorage); |
There was a problem hiding this comment.
I'm thinking we might need to update the ExternalStroageDataConverter to be used regardless of it external storage is enabled or not AND perform the TMRPL1005 check when not configured. Otherwise clients calls will not deserialize properly and probably get a not as helpful message.
| info -> new WorkflowExecutionMetadata(info, clientOptions.getDataConverter())); | ||
| info -> | ||
| new WorkflowExecutionMetadata( | ||
| info, workflowConverter(info.getExecution(), info.getType().getName()))); |
There was a problem hiding this comment.
This is gaining a WorkflowSerializationContext when it did not have one before. That would be changing the semantics of this API call. It is probably the right thing to do, but shouldn't be slipped in as an undocumented bug fix. So either construct one without the serialization context or we should document it.
What was changed
WorkflowClientInternalImpl,HeartbeatContextImpl, andActivityWorker.Why?
Checklist