Skip to content

Commit f07cdfd

Browse files
committed
Merge master into fix-flaky-SchedulerManager-close-deadlock
Preserve the upstream bounded scheduler shutdown implementation and retain the membership-lock regression test.
2 parents bb076cd + 0e76cf3 commit f07cdfd

39 files changed

Lines changed: 1118 additions & 206 deletions

File tree

.github/actions/setup-gradle/action.yml

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#
1919

2020
name: Setup Gradle
21-
description: Sets up Gradle with Develocity or public build scan publishing by default
21+
description: Sets up Gradle with runner memory defaults and build scans for public repositories
2222
inputs:
2323
develocity-access-key:
2424
description: 'Develocity access key for authenticated build scans'
@@ -28,6 +28,10 @@ inputs:
2828
description: 'Whether to publish build scans or use Develocity when the access key is set'
2929
required: false
3030
default: 'true'
31+
memory-profile:
32+
description: 'Gradle memory profile: auto (low-memory on Linux runners with up to 8 GiB RAM), low-memory, or standard'
33+
required: false
34+
default: 'auto'
3135
cache-read-only:
3236
description: 'Whether the Gradle cache is read-only'
3337
required: false
@@ -40,7 +44,7 @@ runs:
4044
using: composite
4145
steps:
4246
- name: Set Develocity Project ID and configure custom settings
43-
if: ${{ inputs.develocity-access-key != '' && inputs.build-scan-publish == 'true' }}
47+
if: ${{ inputs.develocity-access-key != '' && inputs.build-scan-publish == 'true' && github.event.repository.visibility == 'public' }}
4448
shell: bash
4549
run: |
4650
mkdir -p ~/.gradle
@@ -49,23 +53,29 @@ runs:
4953
grep -q 'systemProp.scan.uploadInBackground=' ~/.gradle/gradle.properties || echo systemProp.scan.uploadInBackground=false >> ~/.gradle/gradle.properties
5054
5155
- name: Setup Gradle with Develocity
52-
if: ${{ inputs.develocity-access-key != '' && inputs.build-scan-publish == 'true' }}
56+
if: ${{ inputs.develocity-access-key != '' && inputs.build-scan-publish == 'true' && github.event.repository.visibility == 'public' }}
5357
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
5458
with:
5559
develocity-injection-enabled: true
5660
develocity-url: https://develocity.apache.org
5761
# expected format is develocity.apache.org:<access-key>
5862
develocity-access-key: ${{ inputs.develocity-access-key }}
59-
build-scan-publish: ${{ inputs.build-scan-publish }}
63+
build-scan-publish: ${{ inputs.build-scan-publish == 'true' && github.event.repository.visibility == 'public' }}
6064
cache-read-only: ${{ inputs.cache-read-only }}
6165
add-job-summary: ${{ inputs.add-job-summary }}
6266

6367
- name: Setup Gradle
64-
if: ${{ !(inputs.develocity-access-key != '' && inputs.build-scan-publish == 'true') }}
68+
if: ${{ !(inputs.develocity-access-key != '' && inputs.build-scan-publish == 'true' && github.event.repository.visibility == 'public') }}
6569
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
6670
with:
67-
build-scan-publish: ${{ inputs.build-scan-publish }}
71+
build-scan-publish: ${{ inputs.build-scan-publish == 'true' && github.event.repository.visibility == 'public' }}
6872
build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service'
6973
build-scan-terms-of-use-agree: 'yes'
7074
cache-read-only: ${{ inputs.cache-read-only }}
71-
add-job-summary: ${{ inputs.add-job-summary }}
75+
add-job-summary: ${{ inputs.add-job-summary }}
76+
77+
- name: Configure Gradle memory
78+
shell: bash
79+
env:
80+
MEMORY_PROFILE: ${{ inputs.memory-profile }}
81+
run: bash "$GITHUB_ACTION_PATH/configure-memory.sh" "$MEMORY_PROFILE"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
#
20+
21+
set -euo pipefail
22+
23+
profile="${1:-auto}"
24+
case "$profile" in
25+
auto)
26+
profile=standard
27+
if [[ -r /proc/meminfo ]]; then
28+
memory_kib=$(awk '/^MemTotal:/ { print $2 }' /proc/meminfo)
29+
if [[ "$memory_kib" =~ ^[0-9]+$ ]] && (( memory_kib > 0 && memory_kib <= 8 * 1024 * 1024 )); then
30+
profile=low-memory
31+
fi
32+
fi
33+
;;
34+
low-memory|standard) ;;
35+
*) echo "Unknown Gradle memory profile: $profile" >&2; exit 1 ;;
36+
esac
37+
38+
echo "Gradle memory profile: $profile"
39+
if [[ "$profile" == low-memory ]]; then
40+
gradle_dir="${GRADLE_USER_HOME:-$HOME/.gradle}"
41+
mkdir -p "$gradle_dir"
42+
# Bound workers across projects as well as forks within each test task. Leave
43+
# room for native JVM memory and containers, and recycle accumulated test state.
44+
printf '\n' >> "$gradle_dir/gradle.properties"
45+
cat >> "$gradle_dir/gradle.properties" <<'EOF'
46+
org.gradle.jvmargs=-Xmx2g -Xss2m -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
47+
org.gradle.workers.max=2
48+
testMaxParallelForks=2
49+
testForkEvery=50
50+
EOF
51+
fi

.github/workflows/ci-go-functions.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,15 @@ concurrency:
3131
group: ${{ github.workflow }}-${{ github.ref }}
3232
cancel-in-progress: true
3333

34+
# Do not depend on the repository or enterprise default token permissions.
35+
permissions:
36+
contents: read
37+
3438
jobs:
3539
preconditions:
40+
permissions:
41+
contents: read
42+
pull-requests: read # List changed files and check whether the PR is ready.
3643
name: Preconditions
3744
runs-on: ubuntu-24.04
3845
outputs:

.github/workflows/ci-python-functions.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,15 @@ concurrency:
3333
group: ${{ github.workflow }}-${{ github.ref }}
3434
cancel-in-progress: true
3535

36+
# Do not depend on the repository or enterprise default token permissions.
37+
permissions:
38+
contents: read
39+
3640
jobs:
3741
preconditions:
42+
permissions:
43+
contents: read
44+
pull-requests: read # List changed files and check whether the PR is ready.
3845
name: Preconditions
3946
runs-on: ubuntu-24.04
4047
outputs:

.github/workflows/ci-semantic-pull-request.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ concurrency:
2929
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.number }}
3030
cancel-in-progress: true
3131

32+
permissions:
33+
pull-requests: read
34+
3235
jobs:
3336
main:
3437
name: Check pull request title

.github/workflows/codeql.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ env:
3636
jobs:
3737
analyze:
3838
# only run on push and schedule in apache/pulsar repo
39-
if: ${{ github.repository == 'apache/pulsar' || github.event_name == 'workflow_dispatch' }}
39+
if: ${{ (github.repository == 'apache/pulsar' || github.event_name == 'workflow_dispatch') && (github.event.repository.visibility == 'public' || vars.CI_ENABLE_CODEQL == 'true') }}
4040
name: Analyze
4141
runs-on: 'ubuntu-latest'
4242
timeout-minutes: 360

.github/workflows/pulsar-ci-flaky.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,16 @@ env:
8181
ARTIFACT_RETENTION_DAYS: 3
8282
JDK_DISTRIBUTION: corretto
8383

84+
# Do not depend on the repository or enterprise default token permissions.
85+
permissions:
86+
contents: read
87+
8488
jobs:
8589
preconditions:
90+
permissions:
91+
contents: read
92+
pull-requests: read # List changed files and check whether the PR is ready.
93+
actions: write # Cancel scheduled runs in forks.
8694
name: Preconditions
8795
runs-on: ubuntu-24.04
8896
outputs:

.github/workflows/pulsar-ci.yaml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,16 @@ env:
7676
ARTIFACT_RETENTION_DAYS: 3
7777
JDK_DISTRIBUTION: corretto
7878

79+
# Do not depend on the repository or enterprise default token permissions.
80+
permissions:
81+
contents: read
82+
7983
jobs:
8084
preconditions:
85+
permissions:
86+
contents: read
87+
pull-requests: read # List changed files and check whether the PR is ready.
88+
actions: write # Cancel scheduled runs in forks.
8189
name: Preconditions
8290
runs-on: ubuntu-24.04
8391
outputs:
@@ -276,8 +284,8 @@ jobs:
276284
- name: Tune Runner VM
277285
uses: ./.github/actions/tune-runner-vm
278286

279-
- name: Clean Disk when tracing test resource cleanup
280-
if: ${{ env.TRACE_TEST_RESOURCE_CLEANUP != 'off' }}
287+
- name: Clean Disk for replication tests or resource tracing
288+
if: ${{ matrix.group == 'BROKER_GROUP_5' || env.TRACE_TEST_RESOURCE_CLEANUP != 'off' }}
281289
uses: ./.github/actions/clean-disk
282290

283291
- name: Setup ssh access to build runner VM
@@ -638,6 +646,9 @@ jobs:
638646
action: wait
639647

640648
pulsar-test-latest-version-image:
649+
permissions:
650+
contents: read
651+
security-events: write # Upload scheduled container scan results.
641652
name: Build pulsar-test-latest-version Docker image
642653
runs-on: ubuntu-24.04
643654
timeout-minutes: 60
@@ -855,7 +866,8 @@ jobs:
855866
runs-on: ubuntu-24.04
856867
timeout-minutes: 60
857868
needs: ['preconditions', 'unit-tests']
858-
if: ${{ (needs.preconditions.outputs.java_non_tests == 'true' || github.event_name != 'pull_request') && ((github.event_name == 'pull_request' && github.base_ref == 'master') || (github.event_name != 'pull_request' && github.ref_name == 'master')) }}
869+
# Private repositories need Code Security enabled before opting in.
870+
if: ${{ (github.event.repository.visibility == 'public' || vars.CI_ENABLE_CODEQL == 'true') && (needs.preconditions.outputs.java_non_tests == 'true' || github.event_name != 'pull_request') && ((github.event_name == 'pull_request' && github.base_ref == 'master') || (github.event_name != 'pull_request' && github.ref_name == 'master')) }}
859871
permissions:
860872
actions: read
861873
contents: read
@@ -916,6 +928,8 @@ jobs:
916928
# protected_branches section for master branch required_status_checks.
917929
# It depends on all other jobs in this workflow.
918930
pulsar-ci-checks-completed:
931+
permissions:
932+
actions: write # Delete intermediate build artifacts.
919933
name: "Pulsar CI checks completed"
920934
# run always, but skip for other repositories than apache/pulsar when a scheduled workflow is cancelled
921935
# this is to allow the workflow scheduled jobs to show as cancelled instead of failed since scheduled

CODING.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@ Pulsar relies heavily on `CompletableFuture`; prefer it over `ListenableFuture`
8787
return firstAsync(arg).thenCompose(v -> secondAsync(v));
8888
```
8989
- **Converting a synchronous-throwing method to a failed future is not mechanical** — some callers rely
90-
on the throw happening *before* the async work starts, so evaluate each call site. Use a shared
91-
`checkArgumentAsync` helper (in `FutureUtil`) to validate without duplicating try/catch.
90+
on the throw happening *before* the async work starts, so evaluate each call site. Use shared
91+
`FutureUtil` helpers, such as `supplySafely` for invoking a `Supplier<CompletableFuture<T>>`,
92+
instead of duplicating try/catch or null-future handling.
9293

9394
- **Limit concurrency and handle backpressure.** Firing many async operations at once can overwhelm the
9495
system. Options:

CONTRIBUTING.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,23 @@ Other test-related properties: `-PtestJavaVersion=17` (run tests on a different
105105
`-PtestRetryCount=N`, `-PtestFailFast=true|false`, `-PprotobufVersion=4.31.1` (protobuf v4
106106
compatibility tests).
107107

108+
Test JVMs default to a `1300m` heap and up to four forks per task. Override these with
109+
`-PtestMaxHeapSize=1500m` and `-PtestMaxParallelForks=2`; `--max-workers=2` also limits
110+
concurrent workers across projects. Task-specific limits, such as the integration tests' single
111+
fork and `1G` heap, take precedence.
112+
113+
Use `-PtestForkEvery=50` to replace a test JVM after 50 detected test classes, limiting state
114+
retained across classes. The default is `0` (no class limit). Gradle counts candidate classes
115+
passed to test workers before TestNG group filtering, so classes excluded by groups still count
116+
toward each batch. `--tests` can narrow candidates, but wildcard filters may still count classes
117+
that execute no matching tests. Test methods, data-provider rows and factory instances do not
118+
count separately.
119+
Tasks using TestNG XML suites and async-profiler keep recycling disabled; task-specific
120+
isolation, such as SASL's one class per worker, takes precedence.
121+
122+
Test JVMs write heap dumps on heap exhaustion to `/tmp/java_pid<PID>.hprof`, collected by CI's
123+
existing failure artifacts. Set `-PtestHeapDumpPath=<existing-directory>` to use another directory.
124+
108125
Failed tests are retried once by default (`testRetryCount=1`; `0` when running inside the IDE). When
109126
running tests locally, prefer **`-PtestRetryCount=0`** to catch failures (including flakiness) early
110127
instead of having retries mask them.
@@ -333,6 +350,24 @@ locally. (`integrationTest` also accepts `-PtestGroups` / `-PexcludedTestGroups`
333350

334351
### Running the full CI pipeline (Personal CI)
335352

353+
The shared `setup-gradle` action automatically selects a smaller memory profile on Linux runners
354+
with up to 8 GiB of physical RAM: a `2g` Gradle heap, two workers across projects, up to two test
355+
forks per task, and new test workers after 50 detected classes. Larger runners retain the usual
356+
settings. The action's `memory-profile` input accepts `auto` (default), `low-memory` (force the
357+
smaller profile), or `standard` (leave the memory settings unchanged). Command-line `-Dorg.gradle.jvmargs`,
358+
`--max-workers` and `-Ptest*` options can override the profile settings.
359+
360+
Develocity injection and build-scan publishing are disabled when the workflow repository is
361+
private or its visibility is unavailable, even if an access key is configured. Public repositories
362+
can also disable them with the action's `build-scan-publish: 'false'` input. CodeQL runs by default
363+
for public repositories; private repositories with GitHub Code Security enabled can opt in by
364+
setting the repository variable `CI_ENABLE_CODEQL=true`.
365+
366+
The CI workflows declare the token permissions needed by their jobs, including reading PR changes.
367+
This supports repositories whose organization or enterprise enforces read-only default workflow
368+
permissions. Explicit permissions do not override restrictions on tokens for fork pull requests or
369+
enterprise policies that prohibit particular actions.
370+
336371
The full test suite is large and slow to run locally. While iterating on a change, run only the
337372
narrowly-scoped tests relevant to the change (a single test class or package, see above) rather than
338373
a module's entire test task. To validate a larger change against the **full** CI pipeline, do not run

0 commit comments

Comments
 (0)