ci(java): remove MariaDB full integration lane (#15827)

Keep the complete MySQL 8.4 integration profile and per-fork isolation. Retain JDBC precision and CAS coverage on the supported database and align workflow guards and documentation.

Co-authored-by: Qwen-Coder <[email protected]>
This commit is contained in:
jinyeandQwen-Coder authored and GitHub committed 2026-10-29 06:09:57 +00:00
1 parent e43dc4a911
commit b969de851a
15 files changed
+133 -155

No files matched your search

+10 -43
View File
@@ -625,33 +625,14 @@ jobs:
run: 'node scripts/check-flyway-migrations.js packages/managed-agent-server packages/sdk-java/qwencode'
mysql-integration:
name: 'Runtime Broker and Managed Agent ${{ matrix.label }} / Java 21'
name: 'Runtime Broker and Managed Agent MySQL / Java 21'
if: "${{ inputs.tier != 'fast' }}"
# Pool routing per the flyway job (#13245): hosted queue starvation fails
# a zero-step job outright (#13623), the pool does not. The job is
# pool-safe: the database is a per-leg container on a random host port,
# both Maven runs use a job-local repository, and only the shared cache
# write-back holds the test job's per-host sdk-java lock.
# Both legs run the same non-Hosted *IT family, as before #14936 removed
# the MariaDB service. The MariaDB leg is what keeps the published
# MariaDB-only V12 repair (RTRIM, error 1901) and the schema catalog's
# MariaDB branches falsifiable in CI (#14971).
runs-on: '${{ (github.repository == ''QwenLM/qwen-code'' && vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'' && (github.event_name != ''pull_request'' && github.event_name != ''pull_request_review'' || github.event.pull_request.head.repo.full_name == github.repository || contains(fromJSON(''["OWNER","MEMBER","COLLABORATOR"]''), github.event.pull_request.author_association))) && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || fromJSON(''["ubuntu-latest"]'') }}'
strategy:
# One engine's failure must not cancel the other leg's evidence.
fail-fast: false
matrix:
include:
- database: 'mysql'
label: 'MySQL'
image: 'mysql:8.4.6'
client: 'mysql'
engine_args: '--innodb-use-native-aio=0 --skip-log-bin'
- database: 'mariadb'
label: 'MariaDB'
image: 'mariadb:10.11.18'
client: 'mariadb'
engine_args: '--innodb-use-native-aio=0'
# On the pool this job takes the per-host sdk-java lock only for its
# shared Maven cache write-back: both Maven runs resolve from a job-local
# repository, so holding the lock across the whole integration suite
@@ -663,10 +644,7 @@ jobs:
# not as an expected duration.
timeout-minutes: 310
env:
MYSQL_CONTAINER: 'qwen-code-sdk-java-${{ matrix.database }}-${{ github.run_id }}-${{ github.run_attempt }}'
DATABASE_IMAGE: '${{ matrix.image }}'
DATABASE_CLIENT: '${{ matrix.client }}'
DATABASE_ENGINE_ARGS: '${{ matrix.engine_args }}'
MYSQL_CONTAINER: 'qwen-code-sdk-java-mysql-${{ github.run_id }}-${{ github.run_attempt }}'
steps:
- name: 'Restore workspace ownership'
if: "${{ runner.environment == 'self-hosted' }}"
@@ -847,7 +825,7 @@ jobs:
# and fail to boot with `io_setup() failed with EAGAIN` (#14416).
# Service containers cannot take a command, so the database starts here
# instead, with native AIO disabled and its datadir on tmpfs. The MySQL
# leg also binary-logs by default into that uncapped tmpfs, and this
# server binary-logs by default into that uncapped tmpfs, and this
# disposable container has no replica and nothing reads the log.
# Each fork needs its own instance: lock tests assert server-wide
# InnoDB deadlock counters, which separate schemas cannot isolate.
@@ -855,39 +833,28 @@ jobs:
shell: 'bash'
run: |-
set -euo pipefail
engine_args=()
if [[ -n "${DATABASE_ENGINE_ARGS}" ]]; then
read -r -a engine_args <<< "${DATABASE_ENGINE_ARGS}"
fi
for fork in 1 2; do
container="${MYSQL_CONTAINER}-${fork}"
docker run --detach --name "${container}" \
--publish '127.0.0.1::3306' \
--mount type=tmpfs,destination=/var/lib/mysql \
--env MYSQL_ROOT_PASSWORD='runtime-broker' \
"${DATABASE_IMAGE}" "${engine_args[@]}"
# MariaDB ships healthcheck.sh; the MySQL image does not, so each
# leg waits on the readiness signal its own image provides.
if [[ "${DATABASE_CLIENT}" == 'mariadb' ]]; then
health=(docker exec "${container}" healthcheck.sh --connect --innodb_initialized)
else
health=(docker exec "${container}" mysqladmin ping --host=127.0.0.1 --user=root --password=runtime-broker --silent)
fi
# A probe that is itself broken (image bump, renamed healthcheck.sh)
mysql:8.4.6 --innodb-use-native-aio=0 --skip-log-bin
# A probe that is itself broken (image bump, missing mysqladmin)
# is otherwise indistinguishable from a server still starting.
probe_out="${RUNNER_TEMP:-/tmp}/readiness-probe-${fork}.out"
for attempt in {1..60}; do
if "${health[@]}" >"${probe_out}" 2>&1; then
if docker exec "${container}" mysqladmin ping --host=127.0.0.1 --user=root --password=runtime-broker --silent >"${probe_out}" 2>&1; then
break
fi
if ! docker inspect --format '{{.State.Running}}' "${container}" | grep -qx true; then
docker logs "${container}" || true
echo "::error::${DATABASE_IMAGE} container stopped during startup; last ${DATABASE_CLIENT} readiness probe said: $(cat "${probe_out}")"
echo "::error::MySQL container stopped during startup; last mysqladmin readiness probe said: $(cat "${probe_out}")"
exit 1
fi
if [[ "${attempt}" == '60' ]]; then
docker logs "${container}" || true
echo "::error::${DATABASE_IMAGE} not ready after 60 probes; last ${DATABASE_CLIENT} readiness probe said: $(cat "${probe_out}")"
echo "::error::MySQL not ready after 60 probes; last mysqladmin readiness probe said: $(cat "${probe_out}")"
exit 1
fi
sleep 2
@@ -902,7 +869,7 @@ jobs:
# gate, not the server packet limit, is what the test exercises.
# Set both disposable containers before their first JDBC connections.
for fork in 1 2; do
docker exec "${MYSQL_CONTAINER}-${fork}" sh -c 'MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" '"${DATABASE_CLIENT}"' --user=root --execute="SET GLOBAL max_allowed_packet=134217728; SELECT @@GLOBAL.max_allowed_packet"'
docker exec "${MYSQL_CONTAINER}-${fork}" sh -c 'MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" mysql --user=root --execute="SET GLOBAL max_allowed_packet=134217728; SELECT @@GLOBAL.max_allowed_packet"'
done
- name: 'Set up Node.js 22 (hosted)'
if: "${{ runner.environment == 'github-hosted' }}"
@@ -1088,7 +1055,7 @@ jobs:
if: 'failure() || cancelled()'
uses: 'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a' # v7.0.1
with:
name: 'sdk-java-test-reports-${{ matrix.database }}-integration-java-21'
name: 'sdk-java-test-reports-mysql-integration-java-21'
path: |
packages/managed-agent-server/target/surefire-reports/*.txt
packages/managed-agent-server/target/surefire-reports/*.dumpstream
@@ -2,6 +2,8 @@
[English](2026-10-20-sdk-java-fair-lock-queue.md) | [简体中文](2026-10-20-sdk-java-fair-lock-queue.zh-CN.md)
Database integration now runs on MySQL only. References below to the former MariaDB integration site retain historical measurements; its unchanged lock, cache write-back and budget rules apply to the retained MySQL job. See the [current database verification scope](managed-runtime-broker-jdbc.md#database-verification-scope).
## Problem
`.github/workflows/sdk-java.yml` serializes every self-hosted Maven step on one
@@ -2,6 +2,8 @@
[English](2026-10-20-sdk-java-fair-lock-queue.md) | [简体中文](2026-10-20-sdk-java-fair-lock-queue.zh-CN.md)
数据库集成现仅运行 MySQL。下文原 MariaDB 集成站点的相关内容保留历史测量记录;其未变的锁、缓存写回和预算规则适用于保留的 MySQL 任务。参见[当前数据库验证范围](managed-runtime-broker-jdbc.zh-CN.md#数据库验证范围)。
## 问题
`.github/workflows/sdk-java.yml` 通过 `flock --wait` 把所有 self-hosted 的
@@ -2,6 +2,8 @@
[English](2026-10-22-mysql-ci-optimization.md) | [简体中文](2026-10-22-mysql-ci-optimization.zh-CN.md)
Current database integration CI runs on MySQL 8.4 only. The two-engine descriptions and measurements below record the original optimization; the retained MySQL job still uses one independent instance per fork. See the [current database verification scope](managed-runtime-broker-jdbc.md#database-verification-scope).
## Problem and evidence
The main nightly [run 39664846210](https://github.com/QwenLM/qwen-code/actions/runs/39664846210) spent 33 minutes 41 seconds executing 1,060 integration tests in 52 classes. Their reported class times sum to 33 minutes 39 seconds, so execution was effectively serial. This excludes unit tests, Maven cache work, and the runner's Java lock wait.
@@ -2,6 +2,8 @@
[English](2026-10-22-mysql-ci-optimization.md) | [简体中文](2026-10-22-mysql-ci-optimization.zh-CN.md)
当前数据库集成 CI 仅运行 MySQL 8.4。下文双引擎描述和测量保留原优化时的记录;保留的 MySQL 任务仍为每个 fork 使用独立实例。参见[当前数据库验证范围](managed-runtime-broker-jdbc.zh-CN.md#数据库验证范围)。
## 问题与证据
main 夜间[运行 39664846210](https://github.com/QwenLM/qwen-code/actions/runs/39664846210) 执行 52 个类、1,060 个集成用例用了 33 分 41 秒。各类报告耗时累计为 33 分 39 秒,说明实际基本串行。这个时间不包含单元测试、Maven 缓存操作和 runner 的 Java 锁等待。
+30 -1
View File
@@ -75,7 +75,36 @@ The repository contract covers:
- settled result reconstruction through a new repository instance; and
- repeatable schema initialization.
The default test suite runs the contract on H2 in MySQL compatibility mode. CI also runs the `mysql-integration` Maven profile against MariaDB through MySQL Connector/J so session-time-zone handling and persisted lease round trips are exercised on a real MySQL-compatible protocol. The same profile can be run locally against a caller-supplied MySQL database.
The default test suite runs the contract on H2 in MySQL compatibility mode. CI runs the complete `mysql-integration` Maven profile against MySQL 8.4 through MySQL Connector/J. The same profile can be run locally against a caller-supplied MySQL database.
### Database verification scope
MySQL is the supported database family and the full integration CI target.
MariaDB was introduced as a real-server regression fixture for the lease-clock
change and later gained its own full CI leg. That additional engine is no longer
an ongoing compatibility target. Historical MariaDB measurements remain valid
records of those runs; retained dialect handling is not a compatibility guarantee.
No production SQL, engine admission, schema or catalog migration changes here.
The database job now has one MySQL 8.4 configuration. Its two Maven forks retain
separate disposable instances because server-wide deadlock counters cannot be
isolated by database name. Native AIO and binary logging stay disabled, and each
instance retains the large-packet budget. The complete unfiltered Maven profile,
non-Hosted report guard, cancellation cleanup and full-tier trigger remain.
Hosted/failover and EventTransport gates keep their existing jobs and coverage.
The MySQL contract retains UTC, +08:00 and -04:00 clock checks and verifies both
precise and whole-second readings. Shared repository fixtures use nonzero epoch
milliseconds to pin binding/session/activity and persisted CAS equality on H2 and
MySQL. Current V1 timestamps are integer epoch milliseconds; the historical
DATETIME/Connector-J fractional-second failure is not claimed as reproduced by
these numeric-path tests.
Validation must show that the single-engine policy rejects the previous matrix,
then exercise the actual startup/configure/verify/cleanup shell bodies with ready,
dead and timed-out instances, including failure of the second fork. Existing
workflow consumers, full-profile guards and selected real-MySQL JDBC contracts
must continue to pass. Full CI acceptance remains required after submission.
## Acceptance criteria
@@ -75,7 +75,28 @@ Repository 契约覆盖:
- 通过新 Repository 实例恢复最终结果;
- Schema 可重复初始化。
默认测试套件在 MySQL 兼容模式的 H2 上运行该契约。CI 还会通过 MySQL Connector/J 在 MariaDB 上运行 `mysql-integration` Maven profile,以便在真实 MySQL 兼容协议上覆盖 session time zone 处理和持久化租约往返读取。同一 profile 也可以在本地对调用方提供的 MySQL 数据库运行。
默认测试套件在 MySQL 兼容模式的 H2 上运行该契约。CI 通过 MySQL Connector/J 在 MySQL 8.4 上运行完整的 `mysql-integration` Maven profile。同一 profile 也可以在本地对调用方提供的 MySQL 数据库运行。
### 数据库验证范围
MySQL 是受支持的数据库系列,也是完整集成 CI 的目标。MariaDB 最初作为租约时钟
改动的真实数据库回归夹具引入,后来增加了独立的全量 CI 分支;现在不再作为持续兼容
目标。历史 MariaDB 测量仍保留为当时运行的记录,留存的方言处理不构成兼容承诺。
本次不修改生产 SQL、引擎准入、schema 或 catalog 迁移行为。
数据库任务收敛为一个 MySQL 8.4 配置。两个 Maven 分叉仍各用独立临时实例,因为
服务器级死锁计数器不能用数据库名隔离。继续关闭 native AIO 和二进制日志,每个实例
保留大包预算。完整、无选择过滤的 Maven profile、非 Hosted 报告守卫、取消清理和
full 档触发条件均保留。Hosted/故障恢复及 EventTransport 门控保留原任务及覆盖。
MySQL 契约保留 UTC、+08:00、-04:00 时钟检查,并验证精确读数和整秒读数。
共享 Repository 夹具使用非零 epoch 毫秒,约束 H2 和 MySQL 上 Binding/Session/活动
时间及持久化 CAS 相等性。当前 V1 时间戳使用整数 epoch 毫秒;这些数值路径测试
不宣称复现历史 DATETIME/Connector-J 小数秒丢失问题。
验证必须证明单引擎策略能拒绝原双引擎矩阵,再对实际启动、配置、验证和清理 shell
脚本覆盖就绪、退出、超时及第二个分叉失败的场景。已有工作流消费者、完整 profile
守卫和选定的真实 MySQL JDBC 契约必须继续通过。提交后仍要求完整 CI 验收。
## 验收标准
+1 -1
View File
@@ -45,7 +45,7 @@ nothing. The skill's environment contract states what the lane provides.
Left under _Not covered_ in the skill, and still owned by `sdk-java.yml`:
- MariaDB / MySQL failsafe integration tests and the hosted harness. Those
- MySQL 8.4 failsafe integration tests and the hosted harness. Those
need a database and a bundled `dist/cli.js`.
- The Java 11 and Java 17 matrix.
- Re-running Java tests through the flakiness gate.
+1 -1
View File
@@ -38,7 +38,7 @@ PR 不付代价。技能的环境约定写明通道提供什么。
在技能里列为 _Not covered_,仍由 `sdk-java.yml` 负责:
- MariaDB / MySQL 的 failsafe 集成测试和 hosted harness。它们需要数据库和
- MySQL 8.4 的 failsafe 集成测试和 hosted harness。它们需要数据库和
打包好的 `dist/cli.js`。
- Java 11 和 Java 17 矩阵。
- 用抖动门重跑 Java 测试。
+5
View File
@@ -296,6 +296,11 @@ reclaim anything. If the database check fails, capacity remains skipped.
- Maven 3.8.9+ (the SpotBugs gate's plugin declares that floor)
- MySQL 8
MySQL is the supported database family; full integration CI uses MySQL 8.4.
MariaDB no longer has a CI lane or an ongoing compatibility guarantee.
Existing dialect handling remains in place; this change does not reject
connections that previously worked or introduce a catalog migration.
Run the packaged CLI with `qwen serve --profile hosted-harness` as a separate
process (the credentialed launch is spelled out in the Full WebShell
dual-path development entry below). It supports durable no-tool Sessions and
@@ -41,8 +41,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/
public final class JdbcRepositoryContract {
private static final Instant START = Instant.parse(
"2026-09-20T00:00:00Z");
private static final Instant HEALTH = START.plusSeconds(30);
"2026-09-20T00:00:00.123Z");
private static final Instant HEALTH = Instant.parse(
"2026-09-20T00:00:30.456Z");
private JdbcRepositoryContract() {
}
@@ -66,6 +66,8 @@ class JdbcRuntimeBrokerMySqlIT {
PreparedStatement timeZone = connection.prepareStatement(
"SET time_zone = '" + offset + "'")) {
timeZone.execute();
JdbcRepositorySupportTest.assertDatabaseClock(
JdbcRepositorySupport.databaseNowPrecise(connection));
JdbcRepositorySupportTest.assertStorageSafeClock(
JdbcRepositorySupport.databaseNow(connection));
}
+2 -2
View File
@@ -5,8 +5,8 @@
*/
// The Java integration-test jobs split `*IT.java` by name: `Hosted*IT` runs
// only in the Hosted MySQL job, and every other `*IT` runs in both engines'
// legs of the mysql-integration job (MySQL 8.4 and MariaDB 10.11).
// only in the Hosted MySQL job, and every other `*IT` runs in the MySQL 8.4
// mysql-integration job.
// Maven still passes when a profile's <includes>, or -Dit.test in MAVEN_ARGS
// or .mvn/maven.config, narrows that selection, so after a job's tests this
// compares the failsafe reports with the source tree of each module: every
+3 -9
View File
@@ -298,20 +298,14 @@ describe('Hosted real-process gates', () => {
expect(job.services.mysql.image).toBe('mysql:8.4.6');
expect(job.services.mysql.ports).toEqual(['3306/tcp']);
expect(job.if).toBe("${{ inputs.tier != 'fast' }}");
// The non-Hosted slice runs once per engine, so the Hosted lane stays the
// only place Hosted*IT executes while MariaDB keeps a real leg (#14971).
// Hosted*IT executes only in the separate Hosted lane.
const nonHostedLane = java.jobs['mysql-integration'];
expect(nonHostedLane.strategy.matrix.include).toContainEqual(
expect.objectContaining({ image: 'mysql:8.4.6' }),
);
expect(nonHostedLane.strategy.matrix.include).toContainEqual(
expect.objectContaining({ image: 'mariadb:10.11.18' }),
);
expect(nonHostedLane.strategy).toBeUndefined();
expect(
nonHostedLane.steps.find(
(step) => step.name === 'Start the database without native AIO',
).run,
).toContain('"${DATABASE_IMAGE}" "${engine_args[@]}"');
).toContain('mysql:8.4.6 --innodb-use-native-aio=0 --skip-log-bin');
// Keep daily coverage and run the same suite before Java publication.
expect(Object.keys(java.on).sort()).toEqual([
'schedule',
+46 -95
View File
@@ -1110,8 +1110,7 @@ describe('SDK Java self-hosted workflow guards', () => {
},
{
name: 'mysql-integration',
artifact:
'sdk-java-test-reports-${{ matrix.database }}-integration-java-21',
artifact: 'sdk-java-test-reports-mysql-integration-java-21',
paths: [
'packages/managed-agent-server/target/surefire-reports/*.txt',
'packages/managed-agent-server/target/surefire-reports/*.dumpstream',
@@ -1133,10 +1132,12 @@ describe('SDK Java self-hosted workflow guards', () => {
'actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a',
);
expect(upload.with.name).toBe(artifact);
const legs = parse(workflow).jobs[name].strategy?.matrix?.include;
if (!Array.isArray(legs) || legs.length > 1) {
// Only a literal single-leg matrix can omit matrix interpolation.
const matrix = parse(workflow).jobs[name].strategy?.matrix;
const legs = matrix?.include;
if (matrix && (!Array.isArray(legs) || legs.length > 1)) {
expect(artifact).toMatch(/\$\{\{\s*matrix\./);
} else {
expect(artifact).not.toMatch(/\$\{\{\s*matrix\./);
}
expect(upload.with.path.trim().split('\n')).toEqual(paths);
expect(upload.with['if-no-files-found']).toBe('ignore');
@@ -1662,38 +1663,16 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
expect(block).not.toContain('refs/pull/');
});
it('runs the non-Hosted family on a MySQL leg and a MariaDB leg', () => {
it('runs the complete non-Hosted family on MySQL only', () => {
const mysql = parse(workflow).jobs['mysql-integration'];
// A MariaDB leg necessarily puts `mariadb` in this job block; what must
// stay absent is a service container, because the pool cannot command one.
expect(mysql.name).toBe('Runtime Broker and Managed Agent MySQL / Java 21');
expect(mysql.services).toBeUndefined();
expect(mysql.strategy['fail-fast']).toBe(false);
// Both legs run the same non-Hosted family. Pinning the include list keeps
// the images and the engine-specific startup arguments explicit: #14936
// dropped the MariaDB lane, which left the published MariaDB-only V12
// repair with no falsifier (#14971).
expect(mysql.strategy.matrix.include).toEqual([
{
database: 'mysql',
label: 'MySQL',
image: 'mysql:8.4.6',
client: 'mysql',
engine_args: '--innodb-use-native-aio=0 --skip-log-bin',
},
{
database: 'mariadb',
label: 'MariaDB',
image: 'mariadb:10.11.18',
client: 'mariadb',
engine_args: '--innodb-use-native-aio=0',
},
]);
expect(mysql.env.MYSQL_CONTAINER).toBe(
'qwen-code-sdk-java-${{ matrix.database }}-${{ github.run_id }}-${{ github.run_attempt }}',
);
expect(mysql.env.DATABASE_IMAGE).toBe('${{ matrix.image }}');
expect(mysql.env.DATABASE_CLIENT).toBe('${{ matrix.client }}');
expect(mysql.env.DATABASE_ENGINE_ARGS).toBe('${{ matrix.engine_args }}');
expect(mysql.strategy).toBeUndefined();
expect(JSON.stringify(mysql)).not.toMatch(/mariadb|matrix\./i);
expect(mysql.env).toEqual({
MYSQL_CONTAINER:
'qwen-code-sdk-java-mysql-${{ github.run_id }}-${{ github.run_attempt }}',
});
const start = mysql.steps.find(
(s) => s.name === 'Start the database without native AIO',
);
@@ -1705,13 +1684,27 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
const verify = mysql.steps.find((s) =>
s.run?.includes('-Pmysql-integration'),
);
const guard = mysql.steps.find(
(step) =>
step.name === 'Check that every non-Hosted integration test class ran',
);
expect(guard.run).toBe(
'node scripts/check-failsafe-reports.js non-hosted packages/managed-agent-server',
);
const command = verify.run
.replace(/\\\n/g, ' ')
.match(/^\s*mvn .*-Pmysql-integration.*$/m)[0];
expect(command).toContain('clean verify checkstyle:check');
expect(command).not.toMatch(
/-D(?:skipTests|skipITs|test|it\.test)(?:=|\s)/,
);
const cleanup = mysql.steps.at(-1);
expect(start.shell).toBe('bash');
// The image and its arguments come from the matrix entry, so both legs
// share one startup script. `${{ }}` must not reach this `run` body: the
// shell tests below execute it verbatim.
// The shell tests below execute this body verbatim.
expect(start.run).not.toContain('${{');
expect(start.run).toContain('"${DATABASE_IMAGE}" "${engine_args[@]}"');
expect(start.run).toContain(
'mysql:8.4.6 --innodb-use-native-aio=0 --skip-log-bin',
);
expect(start.run).toContain("--publish '127.0.0.1::3306'");
expect(start.run).toContain(
'--mount type=tmpfs,destination=/var/lib/mysql',
@@ -1724,14 +1717,6 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
expect(start.run).toContain('for fork in 1 2');
expect(start.run).toContain('for attempt in {1..60}');
expect(start.run).toContain('sleep 2');
// MariaDB ships healthcheck.sh; MySQL does not. Each leg must wait on
// the signal its own image provides instead of assuming one.
expect(start.run).toContain(
'if [[ "${DATABASE_CLIENT}" == \'mariadb\' ]]; then',
);
expect(start.run).toContain(
'healthcheck.sh --connect --innodb_initialized',
);
expect(start.run).toContain(
'mysqladmin ping --host=127.0.0.1 --user=root --password=runtime-broker --silent',
);
@@ -1743,9 +1728,7 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
expect(configure.run).toContain(
'docker exec "${MYSQL_CONTAINER}-${fork}" sh -c',
);
expect(configure.run).toContain(
'MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" \'"${DATABASE_CLIENT}"\'',
);
expect(configure.run).toContain('MYSQL_PWD="${MYSQL_ROOT_PASSWORD}" mysql');
expect(configure.run).toContain('SET GLOBAL max_allowed_packet=134217728');
expect(verify.env.MYSQL_PORT).toBeUndefined();
expect(verify.run).toContain(
@@ -1769,34 +1752,11 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
expect(cleanup.shell).toBe('bash');
});
// Derived from the matrix so the shell probe below executes the strings CI
// really passes; the `toEqual` pin above stays the one literal copy.
// `readiness` has no workflow counterpart (the start step derives it in
// shell from DATABASE_CLIENT), so it stays test-side.
const READINESS = { mysql: 'mysqladmin', mariadb: 'healthcheck.sh' };
const databaseLegs = parse(workflow).jobs[
'mysql-integration'
].strategy.matrix.include.map(
({ database, image, client, engine_args: engineArgs }) => ({
database,
image,
client,
engineArgs,
readiness: READINESS[database],
}),
);
it
.skipIf(process.platform === 'win32')
.each(
databaseLegs.flatMap((leg) =>
['ready', 'dead', 'timeout', 'dead-second', 'timeout-second'].map(
(mode) => ({ ...leg, mode }),
),
),
)(
'executes $database startup and consumers with a $mode container',
({ database, image, client, engineArgs, readiness, mode }) => {
.each(['ready', 'dead', 'timeout', 'dead-second', 'timeout-second'])(
'executes MySQL startup and consumers with a %s container',
(mode) => {
const mysql = parse(workflow).jobs['mysql-integration'];
const start = mysql.steps.find(
(s) => s.name === 'Start the database without native AIO',
@@ -1815,7 +1775,7 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
.replace(/\\\n/g, ' ')
.match(/^\s*mvn .*-Pmysql-integration.*$/m)[0];
const dir = mkdtempSync(join(tmpdir(), 'sdk-java-mysql-'));
const container = `qwen-code-sdk-java-${database}-123-2`;
const container = 'qwen-code-sdk-java-mysql-123-2';
try {
const result = spawnSync(
'bash',
@@ -1832,7 +1792,7 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
case "$1" in
run) attempts=0; return 0 ;;
exec)
if [[ "$3" == "$READINESS" ]]; then
if [[ "$3" == mysqladmin ]]; then
attempts=$((attempts + 1))
if [[ ( "$MODE" == ready || ( "$MODE" == *-second && "$2" == *-1 ) ) && "$attempts" -eq 3 ]]; then
return 0
@@ -1867,11 +1827,7 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
env: {
...process.env,
MODE: mode,
READINESS: readiness,
MYSQL_CONTAINER: container,
DATABASE_IMAGE: image,
DATABASE_CLIENT: client,
DATABASE_ENGINE_ARGS: engineArgs,
GITHUB_ENV: join(dir, 'env'),
RUNNER_TEMP: dir,
TRACE: join(dir, 'trace'),
@@ -1881,20 +1837,15 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
expect(result.error).toBeUndefined();
const trace = readFileSync(join(dir, 'trace'), 'utf8');
expect(trace).toContain(
`run --detach --name ${container}-1 --publish 127.0.0.1::3306 --mount type=tmpfs,destination=/var/lib/mysql --env MYSQL_ROOT_PASSWORD=runtime-broker ${image} ${engineArgs}`,
`run --detach --name ${container}-1 --publish 127.0.0.1::3306 --mount type=tmpfs,destination=/var/lib/mysql --env MYSQL_ROOT_PASSWORD=runtime-broker mysql:8.4.6 --innodb-use-native-aio=0 --skip-log-bin`,
);
// `"$*"` joins argv, so the trace above cannot tell N correctly split
// engine args from one argument containing spaces. The arg count can:
// 11 args precede them in `docker run`, and no other docker call here
// reaches that many. A joined element would report one fewer.
expect(trace).toContain(`ARGC<${11 + engineArgs.split(' ').length}>`);
// Each leg must wait on the readiness signal its own image ships.
expect(trace).toContain(`exec ${container}-1 ${readiness}`);
expect(trace).toContain('ARGC<13>');
expect(trace).toContain(`exec ${container}-1 mysqladmin`);
if (mode === 'ready' || mode.endsWith('-second')) {
expect(trace).toContain(
`run --detach --name ${container}-2 --publish 127.0.0.1::3306 --mount type=tmpfs,destination=/var/lib/mysql --env MYSQL_ROOT_PASSWORD=runtime-broker ${image} ${engineArgs}`,
`run --detach --name ${container}-2 --publish 127.0.0.1::3306 --mount type=tmpfs,destination=/var/lib/mysql --env MYSQL_ROOT_PASSWORD=runtime-broker mysql:8.4.6 --innodb-use-native-aio=0 --skip-log-bin`,
);
expect(trace).toContain(`exec ${container}-2 ${readiness}`);
expect(trace).toContain(`exec ${container}-2 mysqladmin`);
}
if (mode === 'ready') {
expect(result.status, result.stderr).toBe(0);
@@ -1928,9 +1879,9 @@ describe('SDK Java MySQL lane on the ECS pool', () => {
} else {
expect(existsSync(join(dir, 'env'))).toBe(false);
}
expect(
trace.match(new RegExp(`exec \\S+ ${readiness}`, 'g')),
).toHaveLength((second ? 3 : 0) + (mode.startsWith('dead') ? 1 : 60));
expect(trace.match(/exec \S+ mysqladmin/g)).toHaveLength(
(second ? 3 : 0) + (mode.startsWith('dead') ? 1 : 60),
);
expect(trace.match(/SLEEP<2>/g) ?? []).toHaveLength(
(second ? 2 : 0) + (mode.startsWith('dead') ? 0 : 59),
);