Skip to content

Commit 00ec3e4

Browse files
authored
Merge branch 'master' into dependabot/github_actions/actions/setup-go-6
2 parents 7f782d7 + f5df5c4 commit 00ec3e4

41 files changed

Lines changed: 6420 additions & 757 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-native-image.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ jobs:
102102
# path ends in a wildcard because on windows the file ends in '.exe'
103103
- name: Upload executable to workflow
104104
if: ${{ inputs.upload_artifact }}
105-
uses: actions/upload-artifact@v4
105+
uses: actions/upload-artifact@v5
106106
with:
107107
name: ${{ matrix.musl && format('{0}_{1}_musl', matrix.os_family, matrix.arch) || format('{0}_{1}', matrix.os_family, matrix.arch)}}
108108
path: |

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
run: ./gradlew --no-daemon temporal-sdk:testResourceIndependent -x spotlessCheck -x spotlessApply -x spotlessJava -P edgeDepsTest
4343

4444
- name: Publish Test Report
45-
uses: mikepenz/action-junit-report@v5
45+
uses: mikepenz/action-junit-report@v6
4646
if: success() || failure() # always run even if the previous step fails
4747
with:
4848
report_paths: '**/build/test-results/test/TEST-*.xml'
@@ -127,7 +127,7 @@ jobs:
127127
run: ./gradlew --no-daemon :temporal-sdk:virtualThreadTests -x spotlessCheck -x spotlessApply -x spotlessJava
128128

129129
- name: Publish Test Report
130-
uses: mikepenz/action-junit-report@v5
130+
uses: mikepenz/action-junit-report@v6
131131
if: success() || failure() # always run even if the previous step fails
132132
with:
133133
report_paths: '**/build/test-results/test/TEST-*.xml'
@@ -166,7 +166,7 @@ jobs:
166166
run: ./gradlew --no-daemon :temporal-sdk:test --tests '*CloudOperationsClientTest'
167167

168168
- name: Publish Test Report
169-
uses: mikepenz/action-junit-report@v5
169+
uses: mikepenz/action-junit-report@v6
170170
if: success() || failure() # always run even if the previous step fails
171171
with:
172172
report_paths: '**/build/test-results/test/TEST-*.xml'

.github/workflows/nightly-throughput-stress.yml

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ on:
44
schedule:
55
# Run at 3 AM PST (11:00 UTC) - offset from existing nightly
66
- cron: '00 11 * * *'
7-
push:
8-
branches:
9-
- add-nightly-throughput-stress-workflow
107
workflow_dispatch:
118
inputs:
129
duration:
@@ -24,8 +21,15 @@ on:
2421
required: false
2522
default: 360
2623
type: number
24+
is_experiment:
25+
description: 'Mark this run as an experiment (excluded from nightly dashboards)'
26+
required: false
27+
default: false
28+
type: boolean
29+
2730
permissions:
2831
contents: read
32+
id-token: write
2933

3034
env:
3135
# Workflow configuration
@@ -35,11 +39,20 @@ env:
3539
# Logging and artifacts
3640
WORKER_LOG_DIR: /tmp/throughput-stress-logs
3741

42+
# AWS S3 metrics upload ARN
43+
AWS_S3_METRICS_UPLOAD_ROLE_ARN: ${{ vars.AWS_S3_METRICS_UPLOAD_ROLE_ARN }}
44+
3845
# Omes configuration
3946
OMES_REPO: temporalio/omes
4047
OMES_REF: main
4148
RUN_ID: ${{ github.run_id }}-throughput-stress
4249

50+
# Prometheus version
51+
PROM_VERSION: "3.8.0"
52+
53+
# Language
54+
SDK_LANG: "java"
55+
4356
jobs:
4457
throughput-stress:
4558
runs-on: ubuntu-latest-4-cores
@@ -88,6 +101,13 @@ jobs:
88101
- name: Install Temporal CLI
89102
uses: temporalio/setup-temporal@v0
90103

104+
- name: Install Prometheus
105+
run: |
106+
wget -q https://github.com/prometheus/prometheus/releases/download/v${PROM_VERSION}/prometheus-${PROM_VERSION}.linux-amd64.tar.gz
107+
tar xzf prometheus-${PROM_VERSION}.linux-amd64.tar.gz
108+
sudo mv prometheus-${PROM_VERSION}.linux-amd64/prometheus /usr/local/bin/
109+
prometheus --version
110+
91111
- name: Setup log directory
92112
run: mkdir -p $WORKER_LOG_DIR
93113

@@ -114,22 +134,47 @@ jobs:
114134
# to give CI a bit more time for visibility consistency
115135
go run ./cmd run-scenario-with-worker \
116136
--scenario throughput_stress \
117-
--language java \
137+
--language $SDK_LANG \
118138
--version $(pwd)/.. \
119139
--run-id $RUN_ID \
120140
--duration $TEST_DURATION \
121141
--timeout $TEST_TIMEOUT \
122142
--max-concurrent 10 \
143+
--prom-listen-address 127.0.0.1:9091 \
144+
--worker-prom-listen-address 127.0.0.1:9092 \
145+
--prom-instance-addr 127.0.0.1:9090 \
146+
--prom-instance-config \
147+
--prom-export-worker-metrics $RUN_ID.parquet \
123148
--option internal-iterations=10 \
124149
--option continue-as-new-after-iterations=3 \
125150
--option sleep-time=1s \
126151
--option visibility-count-timeout=5m \
127152
--option min-throughput-per-hour=1000 \
128153
2>&1 | tee $WORKER_LOG_DIR/scenario.log
129154
155+
- name: Configure AWS credentials
156+
if: always()
157+
uses: aws-actions/configure-aws-credentials@v4
158+
with:
159+
role-to-assume: ${{ env.AWS_S3_METRICS_UPLOAD_ROLE_ARN }}
160+
aws-region: us-west-2
161+
162+
- name: Upload metrics to S3
163+
if: always()
164+
run: |
165+
DATE=$(date +%Y-%m-%d)
166+
IS_EXPERIMENT="false"
167+
# Set as an experiment if we are not on the main branch or input as an experiment
168+
if [[ "$GH_REF" != "refs/heads/main" || "$IS_EXPERIMENT_INPUT" == "true" ]]; then
169+
IS_EXPERIMENT="true"
170+
fi
171+
echo "Uploading metrics: is_experiment=$IS_EXPERIMENT, language=$SDK_LANG, date=$DATE"
172+
aws s3 cp omes/$RUN_ID.parquet \
173+
"s3://cloud-data-ingest-prod/github/sdk_load_test/is_experiment=$IS_EXPERIMENT/language=$SDK_LANG/date=$DATE/$RUN_ID.parquet"
174+
130175
- name: Upload logs on failure
131176
if: failure() || cancelled()
132-
uses: actions/upload-artifact@v4
177+
uses: actions/upload-artifact@v5
133178
with:
134179
name: throughput-stress-logs
135180
path: ${{ env.WORKER_LOG_DIR }}

.github/workflows/prepare-release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ jobs:
148148

149149
# when no artifact is specified, all artifacts are downloaded and expanded into CWD
150150
- name: Fetch executables
151-
uses: actions/download-artifact@v5
151+
uses: actions/download-artifact@v6
152152

153153
# example: linux_amd64/ -> temporal-test-server_1.2.3_linux_amd64
154154
# the name of the directory created becomes the basename of the archive (*.tar.gz or *.zip) and
@@ -165,7 +165,7 @@ jobs:
165165
run: for dir in *windows*; do zip -r "${dir}.zip" "$dir"; done
166166

167167
- name: Upload release archives
168-
uses: actions/upload-artifact@v4
168+
uses: actions/upload-artifact@v5
169169
with:
170170
name: release-archives
171171
path: |

releases/v1.32.1

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Bugfixes
2+
3+
## Spring Boot
4+
5+
Fixed a bug preventing using the in-memory test server for non-root namespaces.
6+
7+
## Environment Config
8+
9+
Fixed a bug causing an exception to be thrown if the default config file was not present.
10+
11+
# What's Changed
12+
13+
2025-11-26 - 0bfa9103 - fix(spring-boot): use in-memory test server for non-root namespaces (#2736)
14+
2025-11-28 - 6bb0fdf6 - Fix Javadoc for RateLimitsConfigurationProperties (#2729)
15+
2025-11-29 - b06f15d6 - Allow for FileNotFound. Do not error, instead return a default instance (#2739)

temporal-envconfig/src/main/java/io/temporal/envconfig/ClientConfig.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,22 @@
55
import com.fasterxml.jackson.dataformat.toml.TomlMapper;
66
import io.temporal.common.Experimental;
77
import java.io.*;
8+
import java.nio.file.Paths;
89
import java.util.HashMap;
910
import java.util.Map;
1011
import java.util.Objects;
1112

12-
/** ClientConfig represents a client config file. */
13+
/**
14+
* ClientConfig represents a client config file.
15+
*
16+
* <p>The default config file path is OS-specific:
17+
*
18+
* <ul>
19+
* <li>macOS: $HOME/Library/Application Support/temporalio/temporal.toml
20+
* <li>Windows: %APPDATA%\temporalio\temporal.toml
21+
* <li>Linux/other: $HOME/.config/temporalio/temporal.toml
22+
* </ul>
23+
*/
1324
@Experimental
1425
public class ClientConfig {
1526
/** Creates a new builder to build a {@link ClientConfig}. */
@@ -32,13 +43,31 @@ public static ClientConfig getDefaultInstance() {
3243
return new ClientConfig.Builder().build();
3344
}
3445

35-
/** Get the default config file path: $HOME/.config/temporalio/temporal.toml */
3646
private static String getDefaultConfigFilePath() {
3747
String userDir = System.getProperty("user.home");
3848
if (userDir == null || userDir.isEmpty()) {
3949
throw new RuntimeException("failed getting user home directory");
4050
}
41-
return userDir + "/.config/temporalio/temporal.toml";
51+
return getDefaultConfigFilePath(userDir, System.getProperty("os.name"), System.getenv());
52+
}
53+
54+
static String getDefaultConfigFilePath(
55+
String userDir, String osName, Map<String, String> environment) {
56+
if (osName != null) {
57+
String osNameLower = osName.toLowerCase();
58+
if (osNameLower.contains("mac")) {
59+
return Paths.get(userDir, "Library", "Application Support", "temporalio", "temporal.toml")
60+
.toString();
61+
}
62+
if (osNameLower.contains("win")) {
63+
String appData = environment != null ? environment.get("APPDATA") : null;
64+
if (appData == null || appData.isEmpty()) {
65+
throw new RuntimeException("%APPDATA% is not defined");
66+
}
67+
return Paths.get(appData, "temporalio", "temporal.toml").toString();
68+
}
69+
}
70+
return Paths.get(userDir, ".config", "temporalio", "temporal.toml").toString();
4271
}
4372

4473
/**

temporal-envconfig/src/test/java/io/temporal/envconfig/ClientConfigProfileTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
88
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
99
import java.io.*;
10+
import java.nio.file.Paths;
1011
import java.util.Collections;
1112
import org.junit.Assert;
1213
import org.junit.Test;
@@ -302,6 +303,28 @@ public void loadClientConfigProfileMissingFileReturnsDefault() throws IOExceptio
302303
Assert.assertNull(profile.getNamespace());
303304
}
304305

306+
@Test
307+
public void defaultConfigFilePath() {
308+
// macOS: ~/Library/Application Support
309+
Assert.assertEquals(
310+
Paths.get("/Users/test", "Library", "Application Support", "temporalio", "temporal.toml")
311+
.toString(),
312+
ClientConfig.getDefaultConfigFilePath("/Users/test", "Mac OS X", Collections.emptyMap()));
313+
314+
// Windows: %APPDATA%
315+
Assert.assertEquals(
316+
Paths.get("C:/Users/test/AppData/Roaming", "temporalio", "temporal.toml").toString(),
317+
ClientConfig.getDefaultConfigFilePath(
318+
"C:/Users/test",
319+
"Windows 10",
320+
Collections.singletonMap("APPDATA", "C:/Users/test/AppData/Roaming")));
321+
322+
// Linux: ~/.config
323+
Assert.assertEquals(
324+
Paths.get("/home/test", ".config", "temporalio", "temporal.toml").toString(),
325+
ClientConfig.getDefaultConfigFilePath("/home/test", "Linux", Collections.emptyMap()));
326+
}
327+
305328
@Test
306329
public void parseToml() throws IOException {
307330
String toml =

temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowExecutor.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.google.common.annotations.VisibleForTesting;
44
import com.google.protobuf.InvalidProtocolBufferException;
55
import com.google.protobuf.util.Timestamps;
6-
import com.uber.m3.tally.Scope;
76
import io.temporal.api.command.v1.ContinueAsNewWorkflowExecutionCommandAttributes;
87
import io.temporal.api.common.v1.Payloads;
98
import io.temporal.api.history.v1.HistoryEvent;
@@ -14,14 +13,12 @@
1413
import io.temporal.api.update.v1.Input;
1514
import io.temporal.api.update.v1.Request;
1615
import io.temporal.failure.CanceledFailure;
17-
import io.temporal.internal.common.FailureUtils;
1816
import io.temporal.internal.common.ProtobufTimeUtils;
1917
import io.temporal.internal.common.UpdateMessage;
2018
import io.temporal.internal.statemachines.WorkflowStateMachines;
2119
import io.temporal.internal.sync.SignalHandlerInfo;
2220
import io.temporal.internal.sync.UpdateHandlerInfo;
2321
import io.temporal.internal.worker.WorkflowExecutionException;
24-
import io.temporal.worker.MetricsType;
2522
import io.temporal.worker.NonDeterministicException;
2623
import io.temporal.workflow.HandlerUnfinishedPolicy;
2724
import java.util.List;
@@ -63,16 +60,13 @@ final class ReplayWorkflowExecutor {
6360

6461
private final ReplayWorkflowContextImpl context;
6562

66-
private final Scope metricsScope;
67-
6863
public ReplayWorkflowExecutor(
6964
ReplayWorkflow workflow,
7065
WorkflowStateMachines workflowStateMachines,
7166
ReplayWorkflowContextImpl context) {
7267
this.workflow = workflow;
7368
this.workflowStateMachines = workflowStateMachines;
7469
this.context = context;
75-
this.metricsScope = context.getMetricsScope();
7670
}
7771

7872
public void eventLoop() {
@@ -131,12 +125,8 @@ private void completeWorkflow(@Nullable WorkflowExecutionException failure) {
131125

132126
if (context.isCancelRequested()) {
133127
workflowStateMachines.cancelWorkflow();
134-
metricsScope.counter(MetricsType.WORKFLOW_CANCELED_COUNTER).inc(1);
135128
} else if (failure != null) {
136129
workflowStateMachines.failWorkflow(failure.getFailure());
137-
if (!FailureUtils.isBenignApplicationFailure(failure.getFailure())) {
138-
metricsScope.counter(MetricsType.WORKFLOW_FAILED_COUNTER).inc(1);
139-
}
140130
} else {
141131
ContinueAsNewWorkflowExecutionCommandAttributes attributes =
142132
context.getContinueAsNewOnCompletion();
@@ -152,23 +142,17 @@ private void completeWorkflow(@Nullable WorkflowExecutionException failure) {
152142
// This way attributes will need to be carried over in the mutable state and the flow
153143
// generally will be aligned with the flow of other commands.
154144
workflowStateMachines.continueAsNewWorkflow(attributes);
155-
156-
// TODO Issue #1590
157-
metricsScope.counter(MetricsType.WORKFLOW_CONTINUE_AS_NEW_COUNTER).inc(1);
158145
} else {
159146
Optional<Payloads> workflowOutput = workflow.getOutput();
160147
workflowStateMachines.completeWorkflow(workflowOutput);
161-
162-
// TODO Issue #1590
163-
metricsScope.counter(MetricsType.WORKFLOW_COMPLETED_COUNTER).inc(1);
164148
}
165149
}
166150

167151
com.uber.m3.util.Duration d =
168152
ProtobufTimeUtils.toM3Duration(
169153
Timestamps.fromMillis(System.currentTimeMillis()),
170154
Timestamps.fromMillis(context.getRunStartedTimestampMillis()));
171-
metricsScope.timer(MetricsType.WORKFLOW_E2E_LATENCY).record(d);
155+
workflowStateMachines.setPostCompletionEndToEndLatency(d);
172156
}
173157

174158
public void handleWorkflowExecutionCancelRequested(HistoryEvent event) {

temporal-sdk/src/main/java/io/temporal/internal/replay/ReplayWorkflowRunTaskHandler.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,21 @@ public WorkflowTaskResult handleWorkflowTask(
185185
if (workflow.getWorkflowContext() != null) {
186186
result.setVersioningBehavior(workflow.getWorkflowContext().getVersioningBehavior());
187187
}
188+
// Setup post-completion metrics to be applied after task response accepted
189+
String postCompleteCounter = workflowStateMachines.getPostCompletionMetricCounter();
190+
com.uber.m3.util.Duration postCompleteLatency =
191+
workflowStateMachines.getPostCompletionEndToEndLatency();
192+
if (postCompleteCounter != null || postCompleteLatency != null) {
193+
result.setApplyPostCompletionMetrics(
194+
() -> {
195+
if (postCompleteCounter != null) {
196+
metricsScope.counter(postCompleteCounter).inc(1);
197+
}
198+
if (postCompleteLatency != null) {
199+
metricsScope.timer(MetricsType.WORKFLOW_E2E_LATENCY).record(postCompleteLatency);
200+
}
201+
});
202+
}
188203
return result.build();
189204
} finally {
190205
lock.unlock();

0 commit comments

Comments
 (0)