fix(update): make store migration production-safe
这个提交包含在:
父节点
0910cfab12
当前提交
a86b753508
18
Jenkinsfile
vendored
18
Jenkinsfile
vendored
@ -165,6 +165,24 @@ pipeline {
|
||||
ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${PROD_USER}@${PROD_HOST} "docker image prune -f 2>/dev/null || true; docker pull ${latestImage} || exit 1; docker compose -f ${COMPOSE_FILE} up -d --no-deps --force-recreate ${svc} || exit 1; docker image prune -f"
|
||||
"""
|
||||
}
|
||||
|
||||
// Jenkins 不能仅以 docker compose 返回 0 判定部署成功。Spring 服务可能
|
||||
// 在容器启动后因配置或数据库迁移失败进入重启循环,必须等待真实健康端点。
|
||||
def healthPortByService = [
|
||||
'tenant-service': 9001,
|
||||
'update-service': 8084,
|
||||
'xuqm-bugcollect-service': 9006,
|
||||
]
|
||||
def healthPort = healthPortByService[svc]
|
||||
if (healthPort != null) {
|
||||
retry(20) {
|
||||
sleep time: 3, unit: 'SECONDS'
|
||||
bat """
|
||||
chcp 65001 >nul
|
||||
ssh -i "%SSH_KEY%" -o StrictHostKeyChecking=no ${PROD_USER}@${PROD_HOST} "curl -fsS http://127.0.0.1:${healthPort}/actuator/health | grep -q '\\\"status\\\":\\\"UP\\\"'"
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 合并更新 versions.json(只改动本次构建涉及的服务,不覆盖其它服务或 web 条目)
|
||||
|
||||
@ -21,7 +21,7 @@ CREATE TABLE update_store_application (
|
||||
updated_at DATETIME(6) NOT NULL,
|
||||
UNIQUE KEY uk_store_application (app_key, store_type),
|
||||
INDEX idx_store_application_sync (enabled, last_synced_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_credential_profile (
|
||||
id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
@ -37,7 +37,7 @@ CREATE TABLE update_store_credential_profile (
|
||||
created_at DATETIME(6) NOT NULL,
|
||||
updated_at DATETIME(6) NOT NULL,
|
||||
UNIQUE KEY uk_store_credential_purpose (app_key, store_type, purpose)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_secret (
|
||||
secret_ref VARCHAR(256) NOT NULL PRIMARY KEY,
|
||||
@ -46,7 +46,7 @@ CREATE TABLE update_store_secret (
|
||||
key_version VARCHAR(32) NOT NULL,
|
||||
created_at DATETIME(6) NOT NULL,
|
||||
updated_at DATETIME(6) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_version (
|
||||
id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
@ -67,7 +67,7 @@ CREATE TABLE update_store_version (
|
||||
CONSTRAINT fk_store_version_application
|
||||
FOREIGN KEY (store_application_id) REFERENCES update_store_application(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_region_availability (
|
||||
id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
@ -80,7 +80,7 @@ CREATE TABLE update_store_region_availability (
|
||||
CONSTRAINT fk_store_region_version
|
||||
FOREIGN KEY (store_version_id) REFERENCES update_store_version(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_state_event (
|
||||
id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
@ -104,7 +104,7 @@ CREATE TABLE update_store_state_event (
|
||||
CONSTRAINT fk_store_event_version
|
||||
FOREIGN KEY (store_version_id) REFERENCES update_store_version(id)
|
||||
ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_update_link (
|
||||
id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
@ -120,7 +120,7 @@ CREATE TABLE update_store_update_link (
|
||||
CONSTRAINT fk_store_link_app_version
|
||||
FOREIGN KEY (app_version_id) REFERENCES update_app_version(id)
|
||||
ON DELETE RESTRICT
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_notification_policy (
|
||||
id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
@ -134,7 +134,7 @@ CREATE TABLE update_store_notification_policy (
|
||||
created_at DATETIME(6) NOT NULL,
|
||||
updated_at DATETIME(6) NOT NULL,
|
||||
UNIQUE KEY uk_store_notification_policy (app_key)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE update_store_notification_outbox (
|
||||
id VARCHAR(64) NOT NULL PRIMARY KEY,
|
||||
@ -152,4 +152,4 @@ CREATE TABLE update_store_notification_outbox (
|
||||
CONSTRAINT fk_store_notification_event
|
||||
FOREIGN KEY (store_event_id) REFERENCES update_store_state_event(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
package com.xuqm.update.config;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class MigrationSchemaContractTest {
|
||||
|
||||
@Test
|
||||
void storeMonitoringTablesUseLegacyCompatibleCollation() throws IOException {
|
||||
var resource = getClass().getResourceAsStream(
|
||||
"/db/migration/V5__store_monitoring_domain.sql"
|
||||
);
|
||||
assertTrue(resource != null, "V5 migration must be packaged");
|
||||
|
||||
String sql;
|
||||
try (resource) {
|
||||
sql = new String(resource.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
assertEquals(9, occurrences(sql, "CREATE TABLE update_store_"));
|
||||
assertEquals(
|
||||
9,
|
||||
occurrences(sql, "DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"),
|
||||
"Every V5 table must match the collation of existing varchar foreign keys"
|
||||
);
|
||||
}
|
||||
|
||||
private static int occurrences(String value, String token) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while ((offset = value.indexOf(token, offset)) >= 0) {
|
||||
count++;
|
||||
offset += token.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
正在加载...
在新工单中引用
屏蔽一个用户