Skip to content

Commit 3c55ada

Browse files
authored
chore: デプロイ先を東京リージョンに切り替える (#77)
東京側の受け皿は用意済み(データ32件移行・関数3つ・Function URL 公開)。 **このコミットではまだ本番は動かない。** 切り替わるのは frontend/config.js を東京の URL に向けたとき。 変更: - ワークフロー4本の --region / aws-region を ap-northeast-1 へ - スクリプトの既定リージョンを ap-northeast-1 へ - beta-preflight.mjs の直書き URL を東京の mvp-dev へ ⚠️ **リージョンが混ざると、デプロイは成功するのに動かない。** 別リージョンの関数を更新しても本番は変わらず、 テーブルは別リージョンから読めない(F-05 と同じ形)。 → **混在を止めるテストを2件追加**し、旧リージョンに戻して落ちることを確認した。 移行スクリプト(migrate-to-tokyo.mjs)は「どこから移すか」を持つので対象外。
1 parent e19a414 commit 3c55ada

11 files changed

Lines changed: 175 additions & 17 deletions

‎.github/workflows/beta-preflight.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,6 @@ jobs:
4949
uses: aws-actions/configure-aws-credentials@v4
5050
with:
5151
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
52-
aws-region: us-east-1
52+
aws-region: ap-northeast-1
5353
- name: Run AWS closed-beta preflight
5454
run: npm run beta:preflight -- --aws

‎.github/workflows/deploy-api.yml‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ jobs:
5959
echo "--- zip の中身 ---"
6060
unzip -l function.zip
6161
62-
# Lambda functions live in us-east-1. The AWS_REGION secret is a
62+
# Lambda functions live in ap-northeast-1 (2026-08-28 に us-east-1 から移設)。
63+
# The AWS_REGION secret is a
6364
# different region, so target the function region explicitly here
64-
# (a region mismatch causes AccessDenied against the us-east-1 ARN).
65+
# (a region mismatch causes AccessDenied against the ap-northeast-1 ARN).
6566
- name: Deploy to Lambda
6667
run: |
6768
aws lambda update-function-code \
6869
--function-name gutpacer-backend \
6970
--zip-file fileb://function.zip \
70-
--region us-east-1
71+
--region ap-northeast-1

‎.github/workflows/deploy-closed-beta.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
aws lambda update-function-code \
6161
--function-name gutpacer-mvp-dev \
6262
--zip-file fileb://function.zip \
63-
--region us-east-1
63+
--region ap-northeast-1
6464
6565
deploy-notifier:
6666
if: inputs.component == 'notifier'
@@ -84,4 +84,4 @@ jobs:
8484
aws lambda update-function-code \
8585
--function-name gutpacer-notifier \
8686
--zip-file fileb://function.zip \
87-
--region us-east-1
87+
--region ap-northeast-1

‎.github/workflows/deploy-notifier.yml‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,13 @@ jobs:
4343
run: |
4444
zip -j function.zip backend/notifier/index.mjs
4545
46-
# Lambda functions live in us-east-1. The AWS_REGION secret is a
46+
# Lambda functions live in ap-northeast-1 (2026-08-28 に us-east-1 から移設)。
47+
# The AWS_REGION secret is a
4748
# different region, so target the function region explicitly here
48-
# (a region mismatch causes AccessDenied against the us-east-1 ARN).
49+
# (a region mismatch causes AccessDenied against the ap-northeast-1 ARN).
4950
- name: Deploy to Lambda
5051
run: |
5152
aws lambda update-function-code \
5253
--function-name gutpacer-notifier \
5354
--zip-file fileb://function.zip \
54-
--region us-east-1
55+
--region ap-northeast-1

‎scripts/beta-preflight.mjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ import { spawnSync } from "node:child_process";
1313
import { realpathSync } from "node:fs";
1414
import { fileURLToPath } from "node:url";
1515

16-
const REGION = process.env.GUTPACER_AWS_REGION || "us-east-1";
16+
const REGION = process.env.GUTPACER_AWS_REGION || "ap-northeast-1";
1717
const PRODUCTION_URL = process.env.GUTPACER_PRODUCTION_URL || "https://veai.jp/gutpacer/";
1818
const BETA_URL = process.env.GUTPACER_BETA_URL || "https://veai.jp/gutpacer/dev/";
1919
const BETA_API_URL = process.env.GUTPACER_BETA_API_URL ||
20-
"https://3cxmfovepd6mwir4a5jxwtotf40ojdia.lambda-url.us-east-1.on.aws/";
20+
"https://j5tpq3dg665xdb6xuf6m6o2ene0yfmpj.lambda-url.ap-northeast-1.on.aws/";
2121

2222
const RESULTS = [];
2323

‎scripts/create-metrics-table.mjs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ if (!new RegExp(`^${METRICS_TABLE_BASE}-[a-z0-9][a-z0-9-]*$`).test(TABLE_NAME))
4242
process.exit(2);
4343
}
4444

45-
// 既定は us-east-1。GutPacer の既存テーブル(gutpacer-logs / gutpacer-settings)が
46-
// us-east-1 にあり、別リージョンに作ると Lambda から書けない (F-05)。
45+
// 既定は ap-northeast-1。GutPacer のテーブルが ap-northeast-1 にあり、
46+
// 別リージョンに作ると Lambda から書けない (F-05)。
47+
// 2026-08-28 に us-east-1 から移設した。
4748
const REGION_INDEX = process.argv.indexOf('--region');
4849
const REGION = REGION_INDEX >= 0
4950
? process.argv[REGION_INDEX + 1]
50-
: (process.env.AWS_REGION || 'us-east-1');
51+
: (process.env.AWS_REGION || 'ap-northeast-1');
5152

5253
// ガードが正しく拒否することを確認するために、実際にテーブルを作る必要はない。
5354
// --dry-run は全チェックを通したうえで AWS 呼び出しの直前で止まる。

‎scripts/create-v2-tables.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
UpdateContinuousBackupsCommand
99
} from "@aws-sdk/client-dynamodb";
1010

11-
const region = process.env.AWS_REGION || "us-east-1";
11+
const region = process.env.AWS_REGION || "ap-northeast-1";
1212
const logsTable = process.env.NEW_LOGS_TABLE || "gutpacer-logs-v2";
1313
const usersTable = process.env.NEW_USERS_TABLE || "gutpacer-users";
1414
const execute = process.argv.includes("--execute");

‎scripts/migrate-to-tokyo.mjs‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/**
2+
* GutPacer を us-east-1 → ap-northeast-1 へ移す(データ部分)。
3+
*
4+
* node scripts/migrate-to-tokyo.mjs # 下見(何もしない)
5+
* node scripts/migrate-to-tokyo.mjs --apply # 実行
6+
*
7+
* ⚠️ **旧リージョン(us-east-1)には一切書き込まない。読むだけ。**
8+
* 失敗しても現状は壊れない。切り替えは frontend/config.js を
9+
* 東京の Function URL に向けたときに初めて起きる。
10+
*
11+
* 何度実行しても同じ結果になるように書く(テーブルは存在すれば作らない、
12+
* 項目は put で上書き)。
13+
*/
14+
import {
15+
DynamoDBClient, CreateTableCommand, DescribeTableCommand, ListTablesCommand,
16+
ScanCommand, BatchWriteItemCommand, waitUntilTableExists,
17+
} from "@aws-sdk/client-dynamodb";
18+
19+
const FROM = "us-east-1";
20+
const TO = "ap-northeast-1";
21+
const APPLY = process.argv.includes("--apply");
22+
23+
// 旧リージョンで確認した定義。**GSI もTTLも無い、オンデマンド。**
24+
const TABLES = [
25+
{ name: "gutpacer-logs-v2", keys: [["userId", "HASH", "S"], ["fullDate", "RANGE", "S"]] },
26+
{ name: "gutpacer-logs", keys: [["fullDate", "HASH", "S"]] },
27+
{ name: "gutpacer-settings", keys: [["settingKey", "HASH", "S"]] },
28+
{ name: "gutpacer-users", keys: [["userId", "HASH", "S"]] },
29+
];
30+
31+
const src = new DynamoDBClient({ region: FROM });
32+
const dst = new DynamoDBClient({ region: TO });
33+
34+
async function existsInTokyo(name) {
35+
try {
36+
await dst.send(new DescribeTableCommand({ TableName: name }));
37+
return true;
38+
} catch (e) {
39+
if (e.name === "ResourceNotFoundException") return false;
40+
throw e;
41+
}
42+
}
43+
44+
async function createTable(t) {
45+
await dst.send(new CreateTableCommand({
46+
TableName: t.name,
47+
BillingMode: "PAY_PER_REQUEST",
48+
AttributeDefinitions: t.keys.map(([n, , type]) => ({ AttributeName: n, AttributeType: type })),
49+
KeySchema: t.keys.map(([n, kt]) => ({ AttributeName: n, KeyType: kt })),
50+
}));
51+
await waitUntilTableExists({ client: dst, maxWaitTime: 120 }, { TableName: t.name });
52+
}
53+
54+
async function scanAll(client, name) {
55+
const items = [];
56+
let key;
57+
do {
58+
const r = await client.send(new ScanCommand({ TableName: name, ExclusiveStartKey: key }));
59+
items.push(...(r.Items ?? []));
60+
key = r.LastEvaluatedKey;
61+
} while (key);
62+
return items;
63+
}
64+
65+
async function copy(t) {
66+
const items = await scanAll(src, t.name);
67+
if (!APPLY) return { count: items.length, wrote: 0 };
68+
for (let i = 0; i < items.length; i += 25) {
69+
const chunk = items.slice(i, i + 25).map((Item) => ({ PutRequest: { Item } }));
70+
let req = { [t.name]: chunk };
71+
// 未処理分は必ず再送する。**黙って落とさない。**
72+
for (let attempt = 0; attempt < 5 && Object.keys(req).length; attempt++) {
73+
const r = await dst.send(new BatchWriteItemCommand({ RequestItems: req }));
74+
req = r.UnprocessedItems ?? {};
75+
if (Object.keys(req).length) await new Promise((s) => setTimeout(s, 300 * (attempt + 1)));
76+
}
77+
if (Object.keys(req).length) throw new Error(`${t.name}: 書き込めなかった項目が残った`);
78+
}
79+
return { count: items.length, wrote: items.length };
80+
}
81+
82+
console.log(`${FROM} → ${TO}${APPLY ? "" : "(下見。--apply で実行)"}\n`);
83+
let ng = 0;
84+
for (const t of TABLES) {
85+
const has = await existsInTokyo(t.name);
86+
if (!has) {
87+
if (APPLY) { await createTable(t); console.log(` 作成 ${t.name}`); }
88+
else console.log(` 作成予定 ${t.name}`);
89+
} else {
90+
console.log(` 既にある ${t.name}`);
91+
}
92+
if (!APPLY && !has) { console.log(` (テーブルが無いので件数の照合は実行時に)`); continue; }
93+
94+
const { count } = await copy(t);
95+
if (APPLY) {
96+
const after = (await scanAll(dst, t.name)).length;
97+
const ok = after >= count;
98+
if (!ok) ng++;
99+
console.log(` ${count} 件 → 東京 ${after} 件 ${ok ? "✅" : "★不一致"}`);
100+
} else {
101+
console.log(` ${count} 件を移す予定`);
102+
}
103+
}
104+
console.log(ng ? `\n★ ${ng} 件のテーブルで不一致。切り替えないこと。` : "\n照合 OK。旧リージョンはそのまま残してある。");

‎scripts/migrate-to-v2.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const NEW_LOGS_TABLE = process.env.NEW_LOGS_TABLE || "gutpacer-logs-v2";
1717
const NEW_USERS_TABLE = process.env.NEW_USERS_TABLE || "gutpacer-users";
1818
const userId = process.env.MIGRATION_USER_ID;
1919
const execute = process.argv.includes("--execute");
20-
const region = process.env.AWS_REGION || "us-east-1";
20+
const region = process.env.AWS_REGION || "ap-northeast-1";
2121

2222
if (!userId) {
2323
console.error("MIGRATION_USER_ID is required");

‎scripts/retention-report.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { fileURLToPath } from "node:url";
2323

2424
// --- 設定 ---
2525
const LOGS_TABLE = process.env.NEW_LOGS_TABLE || "gutpacer-logs-v2";
26-
const region = process.env.AWS_REGION || "us-east-1";
26+
const region = process.env.AWS_REGION || "ap-northeast-1";
2727
const WEEKS = parseInt(process.env.WEEKS || "4", 10);
2828
const MIN_DAYS_PER_WEEK = 3; // 継続とみなす閾値: 週あたりの最低記録日数
2929

0 commit comments

Comments
 (0)