From e637064cf6f6e300b9aed5a0efbefb003acbb07e Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 26 Sep 2026 01:25:40 +0800 Subject: [PATCH 1/2] fix(channels): repair NULL interval tier labels that broke the channel cache Migration 260 inserted a long-context pricing interval without tier_label. The column is nullable, but the channel repositories scan it as text, so the channel cache and the admin channel pages failed in production. 261 stores '' like every other token interval, and the interval reads coalesce NULL. Co-Authored-By: Claude Opus 5.5 --- .../repository/channel_repo_account_stats_pricing.go | 2 +- backend/internal/repository/channel_repo_pricing.go | 2 +- .../migrations/261_channel_pricing_interval_tier_label.sql | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 backend/migrations/261_channel_pricing_interval_tier_label.sql diff --git a/backend/internal/repository/channel_repo_account_stats_pricing.go b/backend/internal/repository/channel_repo_account_stats_pricing.go index f949db1500b6..0b19dbb7f337 100644 --- a/backend/internal/repository/channel_repo_account_stats_pricing.go +++ b/backend/internal/repository/channel_repo_account_stats_pricing.go @@ -227,7 +227,7 @@ func (r *channelRepository) batchLoadAccountStatsIntervals(ctx context.Context, return nil, nil } rows, err := r.db.QueryContext(ctx, - `SELECT id, pricing_id, min_tokens, max_tokens, tier_label, + `SELECT id, pricing_id, min_tokens, max_tokens, COALESCE(tier_label, ''), input_price, output_price, cache_write_price, cache_write_1h_price, cache_read_price, per_request_price, sort_order, created_at, updated_at FROM channel_account_stats_pricing_intervals diff --git a/backend/internal/repository/channel_repo_pricing.go b/backend/internal/repository/channel_repo_pricing.go index a102035f2490..eb34ed57272b 100644 --- a/backend/internal/repository/channel_repo_pricing.go +++ b/backend/internal/repository/channel_repo_pricing.go @@ -139,7 +139,7 @@ func (r *channelRepository) batchLoadModelPricing(ctx context.Context, channelID // batchLoadIntervals 批量加载多个定价条目的区间 func (r *channelRepository) batchLoadIntervals(ctx context.Context, pricingIDs []int64) (map[int64][]service.PricingInterval, error) { rows, err := r.db.QueryContext(ctx, - `SELECT id, pricing_id, min_tokens, max_tokens, tier_label, + `SELECT id, pricing_id, min_tokens, max_tokens, COALESCE(tier_label, ''), input_price, output_price, cache_write_price, cache_write_1h_price, cache_read_price, input_multiplier, output_multiplier, cache_write_multiplier, cache_read_multiplier, per_request_price, sort_order, created_at, updated_at diff --git a/backend/migrations/261_channel_pricing_interval_tier_label.sql b/backend/migrations/261_channel_pricing_interval_tier_label.sql new file mode 100644 index 000000000000..7e8e6a73d365 --- /dev/null +++ b/backend/migrations/261_channel_pricing_interval_tier_label.sql @@ -0,0 +1,5 @@ +-- 260 inserted a long-context pricing interval without tier_label. The column +-- is nullable, but channel pricing is read as text, so the NULL broke the +-- channel cache and the admin channel pages. Token intervals store ''. +UPDATE channel_pricing_intervals SET tier_label = '' WHERE tier_label IS NULL; +UPDATE channel_account_stats_pricing_intervals SET tier_label = '' WHERE tier_label IS NULL; From 63d7ca6cd165a4c1cf7b47aa81145e56b7190082 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 26 Sep 2026 01:36:44 +0800 Subject: [PATCH 2/2] fix(channels): keep the upstream interval reads; the migration repairs the data Co-Authored-By: Claude Opus 5.5 --- .../internal/repository/channel_repo_account_stats_pricing.go | 2 +- backend/internal/repository/channel_repo_pricing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/internal/repository/channel_repo_account_stats_pricing.go b/backend/internal/repository/channel_repo_account_stats_pricing.go index 0b19dbb7f337..f949db1500b6 100644 --- a/backend/internal/repository/channel_repo_account_stats_pricing.go +++ b/backend/internal/repository/channel_repo_account_stats_pricing.go @@ -227,7 +227,7 @@ func (r *channelRepository) batchLoadAccountStatsIntervals(ctx context.Context, return nil, nil } rows, err := r.db.QueryContext(ctx, - `SELECT id, pricing_id, min_tokens, max_tokens, COALESCE(tier_label, ''), + `SELECT id, pricing_id, min_tokens, max_tokens, tier_label, input_price, output_price, cache_write_price, cache_write_1h_price, cache_read_price, per_request_price, sort_order, created_at, updated_at FROM channel_account_stats_pricing_intervals diff --git a/backend/internal/repository/channel_repo_pricing.go b/backend/internal/repository/channel_repo_pricing.go index eb34ed57272b..a102035f2490 100644 --- a/backend/internal/repository/channel_repo_pricing.go +++ b/backend/internal/repository/channel_repo_pricing.go @@ -139,7 +139,7 @@ func (r *channelRepository) batchLoadModelPricing(ctx context.Context, channelID // batchLoadIntervals 批量加载多个定价条目的区间 func (r *channelRepository) batchLoadIntervals(ctx context.Context, pricingIDs []int64) (map[int64][]service.PricingInterval, error) { rows, err := r.db.QueryContext(ctx, - `SELECT id, pricing_id, min_tokens, max_tokens, COALESCE(tier_label, ''), + `SELECT id, pricing_id, min_tokens, max_tokens, tier_label, input_price, output_price, cache_write_price, cache_write_1h_price, cache_read_price, input_multiplier, output_multiplier, cache_write_multiplier, cache_read_multiplier, per_request_price, sort_order, created_at, updated_at