Skip to content

Go-Live Checklist — Nâng cấp Voucher Management: Kiểm soát, Thống kê & Đối tác

Version: 1.0 Ngày: 2026-03-19 PRD version: v1.1 Tham chiếu: prd.md · dev-spec.md · qa-test-plan.mdComplexity: Large — 4 phases triển khai tuần tự + 1 PERF-FIX prerequisite Database: PostgreSQL 14 — ecommerce schema


E1. Go/No-Go Gates

1.1 Gate tổng quan

#GateTiêu chíPhase áp dụngResponsibleStatus
G-01Code ReviewPR approved bởi ≥ 1 Senior Dev + Tech LeadTất cả phasesTech Lead
G-02Unit TestsCoverage ≥ 80% cho Go handlers mới, all passTất cả phasesDev
G-03QA Sign-off76 test cases pass (theo qa-test-plan.md), 0 Critical/High openPer phaseQA Lead
G-04Performance VerifiedĐạt NFR targets (xem Section 1.2)PERF-FIX, P2, P4Dev + QA
G-05Migration Dry-runChạy thành công trên staging (data production-like)Tất cả phasesDBA / Dev
G-06Rollback Testeddown.sql chạy thành công trên staging, verify data intactTất cả phasesDev
G-07Hasura Metadatahasura metadata apply thành công, permission YAML reviewedTất cả phasesDev
G-08Staging Smoke TestHappy path + 3 edge cases pass trên staging environmentPer phaseQA
G-09PO AcceptancePO confirm UI flow + business logic trên stagingPer phasePO
G-10Monitoring ReadyAlert rules + dashboard panels configured (xem E3.3)Tất cả phasesDevOps

1.2 Performance Gates (NFR)

MetricTargetĐo tạiPhaseGate
Time validation overhead (create_order)< 50ms p95Go handler logP1G-04
Override dashboard list (20 rows)< 500ms p95Browser DevTools → NetworkP1G-04
get_voucher_staff_statistics (1000 staff × 50k vouchers)< 2sEXPLAIN ANALYZE trên stagingP2G-04
get_voucher_usage_cycle_statistics (100k vouchers)< 3sEXPLAIN ANALYZE trên stagingP4G-04
get_voucher_usage_cycle_distribution (100k vouchers)< 3sEXPLAIN ANALYZE trên stagingP4G-04
Campaign detail page (perf-fix)6-8x cải thiện vs hiện tạiBrowser DevTools → total load timePERF-FIXG-04
FE bundle size increase< 50KB gzipped (Chart.js lazy-loaded)npm run build -- --reportP4G-04

1.3 Go/No-Go Decision Matrix

Tình huốngQuyết địnhEscalation
Tất cả gates PASSGO — triển khai theo lịch
G-03 fail (QA) nhưng chỉ Low severityGO with conditions — hotfix trong 48hQA Lead → PO
G-04 fail (Performance)NO-GO — optimize trước khi deployDev → Tech Lead
G-05 fail (Migration dry-run)NO-GO — fix migration, re-testDBA → Tech Lead
G-06 fail (Rollback)NO-GO — rollback PHẢI work trước khi go-liveDev → Tech Lead
ALTER TABLE user_vouchers timeout trên stagingNO-GO — chuyển sang online ALTER approach (xem E2.2)DBA → Tech Lead

E2. Pre-deploy Checks

2.1 Checklist chung (áp dụng mọi phase)

#CheckCommand / ActionExpected
PRE-01Backup databasepg_dump -Fc ecommerce > backup_$(date +%Y%m%d_%H%M%S).dumpFile dump created, size > 0
PRE-02Verify disk spacedf -h /var/lib/postgresql/≥ 20% free (ALTER TABLE cần temp space)
PRE-03Check active connectionsSELECT count(*) FROM pg_stat_activity WHERE datname='ecommerce';< 100 (bình thường)
PRE-04Kill long-running transactionsSELECT pid, age(clock_timestamp(), xact_start), query FROM pg_stat_activity WHERE xact_start < NOW() - INTERVAL '10 minutes' AND state != 'idle';Không có transaction > 10 min (quan trọng cho CREATE INDEX CONCURRENTLY)
PRE-05Verify current schema versionSELECT MAX(version) FROM schema_migrations;Match expected version
PRE-06Hasura health checkcurl -s http://hasura:8080/healthz{"status":"OK"}
PRE-07Go service healthcurl -s http://ecommerce-api:8080/healthz{"status":"ok"}
PRE-08Notify teamPost in Slack #deploy channel"Deploy voucher-enhancement Phase X starting"
PRE-09Verify staging passedCheck staging deploy logGate G-08 PASS
PRE-10Maintenance windowConfirm deploy time slot (21:00-23:00 VN time recommended)Team acknowledged

2.2 Check đặc biệt: ALTER TABLE user_vouchers (Phase 2)

RISK: Bảng user_vouchers có ~5M rows. ALTER TABLE ADD COLUMN thông thường sẽ lock bảng.

#CheckCommand / ActionExpected
PRE-ALT-01Estimate table sizeSELECT pg_size_pretty(pg_total_relation_size('user_vouchers'));Ghi nhận size (dùng ước tính thời gian)
PRE-ALT-02Check blocking locksSELECT relation::regclass, mode, granted FROM pg_locks WHERE relation = 'user_vouchers'::regclass;Không có exclusive lock
PRE-ALT-03Estimate ALTER timeTest trên staging với data production-like< 5 phút cho ADD COLUMN nullable (PostgreSQL 14: instant cho nullable columns)
PRE-ALT-04Plan B: Online ALTERNếu staging > 5 phút → dùng pg_repack hoặc manual approach: CREATE new column → backfill → swapDocumented, tested trên staging

Lưu ý PostgreSQL 14: ALTER TABLE ADD COLUMN ... DEFAULT NULL là instant operation (chỉ metadata change). Column distributor_type TEXT nullable, KHÔNG có DEFAULT value → instant. Tuy nhiên CREATE INDEX trên 5M rows cần thời gian — dùng CONCURRENTLY.

2.3 Check đặc biệt: Expression Index (PERF-FIX)

#CheckCommand / ActionExpected
PRE-IDX-01Verify no concurrent long txSELECT pid, age(clock_timestamp(), xact_start) FROM pg_stat_activity WHERE state = 'active' AND xact_start IS NOT NULL ORDER BY xact_start;Không có tx > 5 min
PRE-IDX-02CREATE INDEX CONCURRENTLY retry planNếu fail do concurrent tx → kill blocking tx → retryDocumented in runbook
PRE-IDX-03Verify index validity sau CREATESELECT indexrelid::regclass, indisvalid FROM pg_index WHERE indexrelid::regclass::text LIKE '%user_vouchers%';indisvalid = true cho index mới

E3. Deploy Steps — Per Phase

3.0 PERF-FIX: Tối ưu Campaign Detail (Prerequisite)

PHẢI deploy TRƯỚC Phase 2 và Phase 4. Phase 1 và Phase 3 không phụ thuộc.

Thời điểm deploy: Sprint X — trước P2/P4 ít nhất 1 ngày (cần verify performance trên production)

StepActionCommand / DetailVerifyRollback
0.1Backuppg_dump -Fc ecommerce > backup_perf_fix.dumpFile created
0.2Run migration: expression indexhasura migrate apply --database-name ecommerce file: {ts}_add_expression_index_user_vouchers.sqlIndex createdDROP INDEX CONCURRENTLY IF EXISTS idx_user_vouchers_issued_or_activated;
0.3Verify index validSELECT indisvalid FROM pg_index WHERE indexrelid = 'idx_user_vouchers_issued_or_activated'::regclass;trueNếu false → DROP + re-CREATE
0.4Apply function optimizationDeploy optimized get_voucher_analytics_summary (merge CTEs, single voucher_logs JOIN)Function replacedRestore original function từ down.sql
0.5Deploy FE: split queryDeploy updated VoucherCampaignDetail.tsx — tách aggregate query riêng, cache countBuild + deploy FERevert FE build
0.6Performance verifyMở campaign detail trên production, check DevTools Network tabLoad time giảm 6-8x so với trước
0.7Monitor 24hCheck error rate + response time dashboardKhông tăng error, response time giảm

3.1 Phase 1: Kiểm soát thời gian (P0 — Urgent)

Dependencies: Không — triển khai độc lập. Có thể deploy song song với PERF-FIX.

Thời điểm deploy: Sprint X (cùng sprint hoặc trước PERF-FIX)

3.1.1 Database Migrations

StepActionMigration FileVerifyRollback
1.1ALTER voucher_campaigns{ts}_alter_voucher_campaigns_min_activation_hours.sqlSELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_name='voucher_campaigns' AND column_name='min_activation_hours'; → integer, default 24ALTER TABLE voucher_campaigns DROP COLUMN IF EXISTS min_activation_hours;
1.2CREATE voucher_activation_overrides{ts}_create_voucher_activation_overrides.sqlSELECT count(*) FROM information_schema.tables WHERE table_name='voucher_activation_overrides'; → 1DROP TABLE IF EXISTS voucher_activation_overrides;
1.3Verify indexesSELECT indexname FROM pg_indexes WHERE tablename='voucher_activation_overrides'; → 4 indexes (user_voucher_id, approved_by, branch_id, created_at)
1.4UPDATE app_setting{ts}_update_app_setting_voucher.sqlSELECT value->'AppSettings'->'VoucherSetting' FROM app_setting WHERE key='AppSettings';{"DefaultMinActivationHours": 24, "DefaultManualVoucherExpiryDays": 30}UPDATE app_setting SET value = value #- '{AppSettings,VoucherSetting}' WHERE key='AppSettings';

3.1.2 Hasura Metadata

StepActionFileVerifyRollback
1.5Track tablepublic_voucher_activation_overrides.yamlHasura Console → Data → voucher_activation_overrides visiblehasura metadata apply (revert YAML)
1.6Update voucher_campaigns permissionspublic_voucher_campaigns.yaml — thêm min_activation_hours vào select permissionsGraphQL query voucher_campaigns { min_activation_hours } → trả giá trịRevert YAML
1.7Verify permissions per roleHasura Console → PermissionsAdmin: CRUD, Manager: select + insert (branch scoped), Staff: select (self)

3.1.3 Backend (Go)

StepActionFilesVerifyRollback
1.8Deploy ecommerce-apiapprove_voucher_override.go (mới), create_order.go (sửa), activate_offline_voucher.go (sửa), pkg/store/app_setting.go, pkg/store/voucher_campaigns.gocurl -s http://ecommerce-api:8080/healthz → OKRevert Docker image to previous tag
1.9Verify action handlerPOST /actions với approve_voucher_override → 200 (valid input) hoặc 400 (invalid)
1.10Test time validationTạo ĐH với voucher kích hoạt < 24h trướcResponse: 400 + message "Voucher chưa đủ thời gian chờ" + remaining_hours

3.1.4 Frontend

StepActionComponentsVerifyRollback
1.11Deploy FE buildVoucherConfigForm, VoucherTimeBlockDialog, VoucherOverrideDashboard, VoucherSettingsSectionBuild success, no console errorsRevert FE build
1.12Smoke test: Config campaignMở tạo campaign → field "Thời gian chờ" hiện, nhập 48h → save → reload → giá trị đúngField hiện + giá trị persist
1.13Smoke test: Block dialogTạo ĐH với voucher kích hoạt 2h trước (campaign set 24h)Dialog chặn hiện + nút "Yêu cầu duyệt" + countdown "Còn 22 giờ"
1.14Smoke test: OverrideLogin Manager → approve override → tạo ĐH thành côngĐH tạo thành công + override record trong dashboard
1.15Smoke test: SettingsSettings → Voucher → thấy DefaultMinActivationHours = 24, DefaultManualVoucherExpiryDays = 30Giá trị hiển thị đúng, thay đổi → save → persist

3.1.5 Post-deploy Verify

#CheckExpected
1.16Tạo ĐH KHÔNG dùng voucherHoạt động bình thường (regression)
1.17Tạo ĐH với voucher đã đủ thời gianThành công, không bị chặn
1.18Tạo ĐH với voucher campaign có min_activation_hours = 0Thành công, skip time check
1.19Kích hoạt voucher offline (manual)expired_at được set = activated_at + DefaultManualVoucherExpiryDays
1.20Override dashboardHiển thị override record vừa tạo, filter by branch/date hoạt động
1.21Monitor error logs 1hKhông có error mới liên quan voucher

3.2 Phase 2: Thống kê nhân viên (P1 — High)

Dependencies: PERF-FIX PHẢI đã deploy thành công (expression index + optimized function).

Thời điểm deploy: Sprint X+1 (sau PERF-FIX ≥ 1 ngày)

3.2.1 Pre-deploy: Verify PERF-FIX

#CheckExpected
DEP-01Expression index tồn tạiSELECT indisvalid FROM pg_index WHERE indexrelid = 'idx_user_vouchers_issued_or_activated'::regclass;true
DEP-02Optimized function activeCampaign detail load time < 3s trên production

3.2.2 Database Migrations

StepActionMigration FileVerifyRollback
2.1ALTER user_vouchers (ADD COLUMN nullable — instant on PG14){ts}_alter_user_vouchers_distributor_type.sqlSELECT column_name FROM information_schema.columns WHERE table_name='user_vouchers' AND column_name='distributor_type'; → existsDROP INDEX IF EXISTS idx_user_vouchers_distributor_type; ALTER TABLE user_vouchers DROP COLUMN IF EXISTS distributor_type;
2.2CREATE INDEX CONCURRENTLYPartial index trên distributor_type WHERE NOT NULLSELECT indisvalid FROM pg_index WHERE indexrelid = 'idx_user_vouchers_distributor_type'::regclass;trueDROP INDEX CONCURRENTLY IF EXISTS idx_user_vouchers_distributor_type;
2.3CREATE return type tablevoucher_staff_statistics_resultSELECT count(*) FROM information_schema.tables WHERE table_name='voucher_staff_statistics_result'; → 1DROP TABLE IF EXISTS voucher_staff_statistics_result;
2.4CREATE FUNCTION{ts}_create_fn_voucher_staff_statistics.sqlSELECT proname FROM pg_proc WHERE proname='get_voucher_staff_statistics'; → existsDROP FUNCTION IF EXISTS get_voucher_staff_statistics; DROP TABLE IF EXISTS voucher_staff_statistics_result;
2.5Performance verifyEXPLAIN ANALYZE SELECT * FROM get_voucher_staff_statistics(NULL, NULL, NULL, NULL);Execution time < 2s (staging with production-like data)

3.2.3 Hasura Metadata

StepActionFileVerifyRollback
2.6Track functionpublic_get_voucher_staff_statistics.yamlGraphQL query get_voucher_staff_statistics(args: {}) → trả dataRevert YAML + hasura metadata apply
2.7Update user_vouchers permissionspublic_user_vouchers.yaml — thêm distributor_type vào selectQuery user_vouchers { distributor_type } → OKRevert YAML

3.2.4 Backend (Go)

StepActionFilesVerifyRollback
2.8Deploy ecommerce-apiactivate_voucher.go (sửa — set distributor_type), pkg/store/user_vouchers.gohealthz → OKRevert Docker image
2.9Verify distributor_type setKích hoạt 1 voucher offline bởi Staff → check user_vouchers.distributor_type'internal_staff'

3.2.5 Frontend

StepActionComponentsVerifyRollback
2.10Deploy FE buildVoucherStaffStatisticsTab, StaffDrillDownDialog, updated VoucherCampaignDetailBuild successRevert FE build
2.11Smoke test: Tab thống kêCampaign detail → tab "Thống kê nhân viên"Table hiện danh sách NV + số liệu + conversion rate
2.12Smoke test: Drill-downClick 1 NV → dialog chi tiếtDanh sách voucher đã phát, status, ngày
2.13Smoke test: ExportClick "Xuất Excel" (< 1000 rows)File download, data đúng

3.2.6 Post-deploy Verify

#CheckExpected
2.14Legacy vouchers (distributor_type = NULL) hiển thị "Nhân viên"Fallback hoạt động
2.15Filter by branch (Manager)Chỉ thấy NV branch mình
2.16Campaign detail page performanceKhông bị chậm hơn sau deploy (regression perf-fix)
2.17Monitor error logs 1hKhông có error mới

3.3 Phase 3: Đối tác Affiliate (P1 — High)

Dependencies: Phase 2 đã deploy (reuse thống kê NV để phân biệt internal vs affiliate).

Thời điểm deploy: Sprint X+1 hoặc X+2 (sau P2)

3.3.1 Pre-deploy: Verify Phase 2

#CheckExpected
DEP-03Column distributor_type tồn tạiSELECT column_name FROM information_schema.columns WHERE table_name='user_vouchers' AND column_name='distributor_type'; → exists
DEP-04Function get_voucher_staff_statistics hoạt độngSELECT * FROM get_voucher_staff_statistics(NULL, NULL, NULL, NULL) LIMIT 1; → returns row
DEP-05Module Affiliate hiện có hoạt độngSELECT count(*) FROM affiliate_user WHERE deleted_at IS NULL; → > 0 (có data)

3.3.2 Database Migrations

StepActionMigration FileVerifyRollback
3.1CREATE voucher_campaign_affiliates{ts}_create_voucher_campaign_affiliates.sqlSELECT count(*) FROM information_schema.tables WHERE table_name='voucher_campaign_affiliates'; → 1DROP TRIGGER IF EXISTS update_voucher_campaign_affiliates_timestamp ON voucher_campaign_affiliates; DROP TABLE IF EXISTS voucher_campaign_affiliates;
3.2Verify indexesSELECT indexname FROM pg_indexes WHERE tablename='voucher_campaign_affiliates'; → 3 indexes (campaign_id, affiliate_user_id, quota_check)
3.3Verify UNIQUE constraintSELECT conname FROM pg_constraint WHERE conrelid='voucher_campaign_affiliates'::regclass AND contype='u';voucher_campaign_affiliates_campaign_id_affiliate_user_id_key
3.4Verify triggerSELECT tgname FROM pg_trigger WHERE tgrelid='voucher_campaign_affiliates'::regclass AND tgname='update_voucher_campaign_affiliates_timestamp'; → exists

3.3.3 Hasura Metadata

StepActionFileVerifyRollback
3.5Track tablepublic_voucher_campaign_affiliates.yamlHasura Console → table visible, permissions correctRevert YAML
3.6Verify permissionsAdmin: CRUD, Manager: select + insert (branch scope qua campaign), Staff: no access

3.3.4 Backend (Go)

StepActionFilesVerifyRollback
3.7Deploy ecommerce-apicreate_draft_voucher_campaign.go (sửa), update_voucher_campaign.go (sửa), activate_offline_voucher.go (sửa — quota check)healthz → OKRevert Docker image
3.8Verify campaign createTạo campaign mới + gán 1 affiliatevoucher_campaign_affiliates có record, distributed_count = 0
3.9Verify atomic quotaKích hoạt voucher cho affiliate có quota = 1, đã phát 0distributed_count = 1, response OK
3.10Verify quota rejectKích hoạt voucher cho affiliate có quota = 1, đã phát 1Response: reject, distributed_count vẫn = 1

3.3.5 Frontend

StepActionComponentsVerifyRollback
3.11Deploy FE buildAffiliateAssignStep, AffiliateSourceBadge, updated VoucherCreate, updated VoucherStaffStatisticsTabBuild successRevert FE build
3.12Smoke test: WizardTạo campaign → step "Đối tác" hiện → search + chọn affiliate → set quota → saveAffiliate assigned, quota displayed
3.13Smoke test: BadgeMở chi tiết ĐH có voucher từ affiliateBadge "Voucher do [Tên KOL] phát" hiện
3.14Smoke test: Report phân biệtTab thống kê NV → filter by "Đối tác"Chỉ hiện affiliate, distributor_type = 'affiliate'

3.3.6 Post-deploy Verify

#CheckExpected
3.15Campaign KHÔNG có affiliateHoạt động bình thường (regression)
3.16Affiliate unlimited quota (NULL)Phát voucher thành công, không bị chặn
3.17Concurrent activation test2 requests đồng thời cho affiliate quota=1 → chỉ 1 thành công
3.18Monitor error logs 1hKhông có error mới

3.4 Phase 4: Chu kỳ sử dụng (P2 — Medium)

Dependencies: PERF-FIX PHẢI đã deploy thành công.

Thời điểm deploy: Sprint X+2 hoặc X+3

3.4.1 Pre-deploy: Verify PERF-FIX

#CheckExpected
DEP-06Expression index tồn tại + validindisvalid = true
DEP-07Campaign detail page < 3sPerformance OK

3.4.2 Database Migrations

StepActionMigration FileVerifyRollback
4.1CREATE return type tablesvoucher_usage_cycle_statistics_result, voucher_usage_cycle_distribution_resultinformation_schema.tables check → 2 tables existDROP TABLE IF EXISTS both
4.2CREATE FUNCTION cycle statistics{ts}_create_fn_voucher_usage_cycle_statistics.sqlSELECT proname FROM pg_proc WHERE proname='get_voucher_usage_cycle_statistics'; → existsDROP FUNCTION IF EXISTS get_voucher_usage_cycle_statistics; DROP TABLE IF EXISTS voucher_usage_cycle_statistics_result;
4.3CREATE FUNCTION cycle distribution{ts}_create_fn_voucher_usage_cycle_distribution.sqlSELECT proname FROM pg_proc WHERE proname='get_voucher_usage_cycle_distribution'; → existsDROP FUNCTION IF EXISTS get_voucher_usage_cycle_distribution; DROP TABLE IF EXISTS voucher_usage_cycle_distribution_result;
4.4Performance verify: statisticsEXPLAIN ANALYZE SELECT * FROM get_voucher_usage_cycle_statistics(NULL, NULL, NULL, NULL, NULL);Execution time < 3s (staging with 100k vouchers)
4.5Performance verify: distributionEXPLAIN ANALYZE SELECT * FROM get_voucher_usage_cycle_distribution(NULL, NULL, NULL, NULL, NULL);Execution time < 3s (staging with 100k vouchers)

3.4.3 Hasura Metadata

StepActionFileVerifyRollback
4.6Track function: statisticspublic_get_voucher_usage_cycle_statistics.yamlGraphQL query trả dataRevert YAML
4.7Track function: distributionpublic_get_voucher_usage_cycle_distribution.yamlGraphQL query trả dataRevert YAML

3.4.4 Frontend

StepActionComponentsVerifyRollback
4.8Deploy FE buildVoucherUsageCycleTab, CycleBucketChart (Chart.js), updated VoucherCampaignDetailBuild success, Chart.js lazy-loadedRevert FE build
4.9Verify bundle sizenpm run build -- --reportChart.js chunk < 50KB gzipped, lazy-loaded
4.10Smoke test: Tab chu kỳCampaign detail → tab "Chu kỳ sử dụng"KPI cards hiện: Trung bình, Trung vị, Nhanh nhất, Tỉ lệ sử dụng
4.11Smoke test: ChartTab chu kỳ → horizontal bar chart7 buckets hiện đúng (0-3, 4-7, 8-14, 15-30, 31-60, 60+, pending)
4.12Smoke test: So sánhChọn 2-3 campaigns → compareBảng so sánh hiện đúng metrics per campaign
4.13Smoke test: ExportClick "Xuất Excel"File download, data đúng

3.4.5 Post-deploy Verify

#CheckExpected
4.14Campaign không có voucher redeemedKPI hiện "—", chart hiện 100% pending
4.15Campaign có 1 voucher redeemedMedian = avg = min = cycle_days của voucher đó
4.16Filter by branch/staff/dateKết quả thay đổi phù hợp
4.17Monitor error logs 1hKhông có error mới

E4. Rollback Plan

4.1 Nguyên tắc rollback

Nguyên tắcChi tiết
Phase độc lậpMỗi phase rollback KHÔNG ảnh hưởng phase khác (trừ dependency chain)
Thứ tự rollbackNgược với deploy: FE → Go → Hasura metadata → Database migration
down.sql bắt buộcMọi migration đều có down.sql tested trên staging
Data integrityRollback KHÔNG xóa user data đã tạo (override records, statistics...) — chỉ xóa schema objects
Quyết định rollbackTech Lead quyết định trong 30 phút nếu có Critical issue post-deploy

4.2 Rollback per phase

Rollback Phase 1

StepActionCommandVerify
R1.1Revert FEDeploy previous FE build tagUI không còn các component P1
R1.2Revert GoDeploy previous Docker image ecommerce-api:prev-taghealthz → OK + time validation removed
R1.3Revert Hasura metadatagit checkout HEAD~1 -- controller/metadata/ + hasura metadata applyTable voucher_activation_overrides untracked
R1.4Revert app_settingUPDATE app_setting SET value = value #- '{AppSettings,VoucherSetting}' WHERE key='AppSettings';VoucherSetting removed
R1.5Drop override tableDROP TABLE IF EXISTS voucher_activation_overrides;Table gone
R1.6Drop columnALTER TABLE voucher_campaigns DROP COLUMN IF EXISTS min_activation_hours;Column gone
R1.7VerifyTạo ĐH với voucher → thành công (no time check)Order created

Rollback Phase 2

StepActionCommandVerify
R2.1Revert FEDeploy previous FE build tagTab thống kê NV không hiện
R2.2Revert GoDeploy previous Docker imagedistributor_type không được set khi activate
R2.3Revert Hasura metadataUntrack function + revert YAMLFunction not accessible via GraphQL
R2.4Drop function + typeDROP FUNCTION IF EXISTS get_voucher_staff_statistics; DROP TABLE IF EXISTS voucher_staff_statistics_result;Function gone
R2.5Drop columnDROP INDEX IF EXISTS idx_user_vouchers_distributor_type; ALTER TABLE user_vouchers DROP COLUMN IF EXISTS distributor_type;Column gone
R2.6VerifyCampaign detail → chỉ có tabs cũKhông có tab "Thống kê nhân viên"

Lưu ý: Rollback P2 KHÔNG ảnh hưởng P1 (independent). Nhưng nếu P3 đã deploy, PHẢI rollback P3 trước vì P3 phụ thuộc P2.

Rollback Phase 3

StepActionCommandVerify
R3.1Revert FEDeploy previous FE build tagWizard không có step "Đối tác", badge không hiện
R3.2Revert GoDeploy previous Docker imageQuota check removed, campaign không insert affiliates
R3.3Revert Hasura metadataUntrack table + revert YAMLTable not accessible via GraphQL
R3.4Drop tableDROP TRIGGER IF EXISTS update_voucher_campaign_affiliates_timestamp ON voucher_campaign_affiliates; DROP TABLE IF EXISTS voucher_campaign_affiliates;Table gone
R3.5VerifyTạo campaign → không có step affiliateNormal flow

Rollback Phase 4

StepActionCommandVerify
R4.1Revert FEDeploy previous FE build tagTab chu kỳ không hiện, Chart.js chunk removed
R4.2Revert Hasura metadataUntrack 2 functions + revert YAMLFunctions not accessible via GraphQL
R4.3Drop functions + typesDROP FUNCTION IF EXISTS get_voucher_usage_cycle_statistics; DROP FUNCTION IF EXISTS get_voucher_usage_cycle_distribution; DROP TABLE IF EXISTS voucher_usage_cycle_statistics_result; DROP TABLE IF EXISTS voucher_usage_cycle_distribution_result;Functions + types gone
R4.4VerifyCampaign detail → chỉ có tabs cũ + tab thống kê NV (P2)Không có tab "Chu kỳ sử dụng"

Lưu ý: Rollback P4 KHÔNG ảnh hưởng các phase khác.

Rollback PERF-FIX

StepActionCommandVerify
RPF.1Revert FEDeploy previous FE build tag (query không split)Single query restored
RPF.2Restore original functionApply down.sql — restore get_voucher_analytics_summary originalFunction reverted
RPF.3Drop expression indexDROP INDEX CONCURRENTLY IF EXISTS idx_user_vouchers_issued_or_activated;Index gone
RPF.4VerifyCampaign detail page loads (slower but functional)Page loads, data correct

CRITICAL: Nếu rollback PERF-FIX thì P2 và P4 KHÔNG ĐƯỢC deploy (dependency chain).

4.3 Rollback Dependency Matrix

Rollback P4 → OK (independent, chỉ DROP functions)
Rollback P3 → OK (independent từ P4, nhưng P2 vẫn cần)
Rollback P2 → PHẢI rollback P3 trước (nếu P3 đã deploy)
Rollback P1 → OK (independent)
Rollback PERF-FIX → PHẢI rollback P2 + P4 trước (nếu đã deploy)

E5. Day-0 / Day-1 Monitoring & Sign-off

5.1 Day-0: Ngày deploy (per phase)

Thời điểmActionResponsibleKênh
T+0 (deploy xong)Chạy smoke tests (Section E3.x.4-5)QAStaging → Production
T+15minCheck error rate dashboardDevOpsGrafana / Datadog
T+30minCheck slow query logDBA / Devpg_stat_statements
T+1hFirst monitoring reportDev → Tech LeadSlack #deploy
T+2hRegression spot-check (tạo ĐH bình thường)QAProduction
T+4hSecond monitoring reportDev → Tech LeadSlack #deploy
T+12h (sáng hôm sau)Overnight error reviewDevError tracking tool

5.2 Day-0: Monitoring Dashboard

PanelMetricAlert ThresholdQuery Source
Voucher Time Block RateSố lần chặn / tổng tạo ĐH có voucher> 50% trong 1h → alert (có thể misconfigured)voucher_activation_overrides count vs orders count
Override RateOverride / tổng bị chặn> 30% trong 1 ngày → alert (có thể lạm dụng)voucher_activation_overrides count
Create Order Latency p95Response time create_order> 2s (baseline + 50ms overhead) → alertGo handler metrics
Staff Statistics Query Timeget_voucher_staff_statistics execution> 5s → alertpg_stat_statements
Cycle Statistics Query Timeget_voucher_usage_cycle_* execution> 5s → alertpg_stat_statements
Affiliate Quota Reject RateQuota reject / total activation attempts> 20% → notify (quota có thể cần tăng)Go handler log
DB Connection PoolActive connections> 80% pool size → alertpg_stat_activity
Error Rate (ecommerce-api)5xx responses / total> 1% → alertService metrics

5.3 Day-1: Ngày +1 sau deploy (per phase)

#CheckResponsibleExpected
D1-01Error reviewDevKhông có error mới liên quan voucher enhancement
D1-02Performance baselineDevResponse times trong target (Section E1.2)
D1-03Data integrityQASpot-check 10 records: overrides, staff stats, affiliate quota, cycle data
D1-04User feedbackPOThu thập feedback từ Manager/Staff dùng thử
D1-05Slow query checkDBASELECT * FROM pg_stat_statements WHERE mean_exec_time > 1000 AND query LIKE '%voucher%' ORDER BY mean_exec_time DESC LIMIT 10;
D1-06Index usage verifyDBASELECT relname, indexrelname, idx_scan FROM pg_stat_user_indexes WHERE relname IN ('voucher_activation_overrides','voucher_campaign_affiliates','user_vouchers') ORDER BY idx_scan; → idx_scan > 0 cho indexes mới
D1-07Table bloat checkDBASELECT relname, n_dead_tup, last_autovacuum FROM pg_stat_user_tables WHERE relname='user_vouchers'; → autovacuum đã chạy, dead tuples OK

5.4 Day-7: Review tuần đầu (sau phase cuối cùng)

#CheckResponsible
D7-01Tổng hợp override statistics — bao nhiêu override, top reason, branch nào nhiều nhấtPO + Tech Lead
D7-02Affiliate quota usage — đối tác nào gần full quota, cần tăng khôngPO
D7-03Performance trend — query times có tăng theo data growth khôngDev
D7-04User adoption — bao nhiêu Manager dùng override dashboard, NV xem thống kêPO
D7-05Bug report tổng hợp — danh sách bug từ production, classify severityQA Lead
D7-06Capacity projection — ước tính khi nào cần partition/archive cho voucher_activation_overridesDBA

5.5 Sign-off Matrix

PhaseQA LeadTech LeadPODBAStatus
PERF-FIXPending
Phase 1Pending
Phase 2Pending
Phase 3Pending
Phase 4Pending
TOÀN BỘ FEATUREPending

Sign-off criteria:

  • QA Lead: Tất cả test cases PASS, 0 open Critical/High bugs
  • Tech Lead: Performance targets MET, rollback tested, monitoring active
  • PO: Business flows verified trên production, user feedback positive
  • DBA: Migrations clean, indexes valid, no bloat/slow queries

Phụ lục: Deploy Timeline tổng quan

Week 1 ─────────────────────────────────────────────────────────
│ Sprint X

├── PERF-FIX ──────────────── Prerequisite (deploy trước)
│   ├── Expression index
│   ├── Function optimization
│   └── FE query split

├── Phase 1 (P0) ─────────── Song song với PERF-FIX
│   ├── ALTER voucher_campaigns
│   ├── CREATE voucher_activation_overrides
│   ├── UPDATE app_setting
│   ├── Go handlers (create_order, activate_offline, override)
│   └── FE: config, dialog, dashboard, settings

Week 2 ─────────────────────────────────────────────────────────
│ Sprint X+1

├── Phase 2 (P1) ─────────── SAU PERF-FIX
│   ├── ALTER user_vouchers (instant PG14)
│   ├── CREATE INDEX CONCURRENTLY
│   ├── CREATE FUNCTION staff_statistics
│   ├── Go handlers (activate_voucher)
│   └── FE: tab thống kê, drill-down, export

├── Phase 3 (P1) ─────────── SAU Phase 2
│   ├── CREATE voucher_campaign_affiliates
│   ├── Go handlers (campaign create/update, quota)
│   └── FE: wizard step, badge, report filter

Week 3-4 ───────────────────────────────────────────────────────
│ Sprint X+2/X+3

└── Phase 4 (P2) ─────────── SAU PERF-FIX (independent of P2/P3)
    ├── CREATE FUNCTION cycle_statistics
    ├── CREATE FUNCTION cycle_distribution
    └── FE: tab chu kỳ, Chart.js chart, export

Phụ lục: Emergency Contacts

RoleTênLiên hệKhi nào
Tech Lead[TBD]Slack / PhoneQuyết định rollback, escalation
DBA[TBD]Slack / PhoneMigration fail, performance issue
PO[TBD]SlackBusiness logic verify, Go/No-Go
DevOps[TBD]SlackDeploy pipeline, monitoring alert
QA Lead[TBD]SlackTest failure, regression report

Tài liệu liên quan:

  • PRD — Requirements + Decision Log
  • UI Spec — Screen map + wireframes
  • Dev Spec — Data model + API + migrations
  • QA Test Plan — 76 test cases
  • Perf-fix — Performance optimization prerequisite