Skip to content

Instantly share code, notes, and snippets.

@sheep-snow
Last active December 26, 2025 05:54
Show Gist options
  • Select an option

  • Save sheep-snow/d6de26a79734a1995afb9b4fc2a250e2 to your computer and use it in GitHub Desktop.

Select an option

Save sheep-snow/d6de26a79734a1995afb9b4fc2a250e2 to your computer and use it in GitHub Desktop.
skyshare-cost-efficient-memos

Upstash Redis読み取り削減の対処案

現状分析

現在の実装では、Cloudflare Workers (workers/src/function.prod.ts) が以下の2つのエンドポイントでUpstash Redisに直接アクセスしています:

  • GET /page/{id} - 個別ページのOGP取得
  • GET /user/{handle} - ユーザーのページID一覧取得(redis.scan()を使用)

利用が増加し、Upstash Redisの無料利用枠では収まらなくなってきました。

対処案の比較

実装コスト Redis削減率 レイテンシ 追加コスト
1. Cloudflare Cache API ⭐ 小 80-95% ✅ 最速 無料
2. Cloudflare KV ⭐⭐ 中 90-99% ✅ 高速 $0.50/月~
3. Cloudflare R2 ⭐⭐⭐ 大 95-99% ⚠️ やや遅 $0.15/月~
4. Durable Objects ⭐⭐⭐ 大 99%+ ✅ 最速 $5/月~
5. 組み合わせ案 ⭐⭐ 中 95%+ ✅ 高速 無料~低

✅ 推奨案1: Cloudflare Cache API(最小変更・無料)

概要

Workers内でCache APIを使い、Upstash Redisからの取得結果を一定期間キャッシュ。

メリット

  • 実装コストが最小(30-50行の追加のみ)
  • 完全無料
  • レイテンシが最も低い(エッジキャッシュ)
  • ✅ Cloudflare標準機能のみ使用

デメリット

  • ⚠️ キャッシュヒット率はアクセスパターンに依存
  • ⚠️ TTL管理が必要

実装イメージ

// workers/src/function.prod.ts に追加
const CACHE_TTL = 60 * 60 * 24; // 24時間(調整可能)

const readPagedb = async ({
    request, redis
}: {
    request: Request,
    redis: Redis
}) => {
    const url = new URL(request.url);
    const cacheKey = new Request(url.toString(), request);
    const cache = caches.default;

    // キャッシュ確認
    let response = await cache.match(cacheKey);
    if (response) {
        return await response.json();
    }

    // 既存のRedis読み取りロジック
    const pathArray = url.pathname.split('/');
    let data: response = { /* ... */ }
    
    // ... 既存の処理 ...

    // レスポンスをキャッシュに保存
    const responseToCache = new Response(JSON.stringify(data), {
        headers: {
            'Content-Type': 'application/json',
            'Cache-Control': `public, max-age=${CACHE_TTL}`,
        },
    });
    await cache.put(cacheKey, responseToCache.clone());
    
    return data;
}

削減効果

  • 同一ページへの再アクセス: 100%削減
  • 人気コンテンツ: 80-95%削減(TTL次第)

✅ 推奨案2: Cloudflare KV + Cache API(高効率)

概要

  • KVをセカンダリストレージとして使用
  • Upstash Redisは書き込み専用(Firebase Functionsから)
  • Workersは主にKVから読み取り、Cache APIで更に加速

メリット

  • 90-99%のRedis削減
  • ✅ KVの無料枠: 100k reads/day(通常十分)
  • ✅ 読み取りレイテンシが安定
  • ✅ 容量制限がほぼない

実装概要

  1. firebase/functions/src/ogpGenerator/addPagedb.tsでKVへも同時書き込み
  2. Workersで優先的にKVから読み取り
  3. KV miss時のみRedisにフォールバック
  4. Cache APIでさらにキャッシュ

wrangler.toml への追加設定

[[kv_namespaces]]
binding = "PAGEDB_KV"
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

[env.prod.kv_namespaces]
binding = "PAGEDB_KV"
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

追加コスト

  • 無料枠内: 100k reads + 1k writes/day + 1GB storage
  • 超過時: $0.50/1M reads, $5/1M writes

💡 推奨案3: 組み合わせ案(Cache + TTL戦略)

概要

Cache APIに加え、アクセスパターンに応じたTTL最適化を実施。

実装戦略

1. /page/{id}エンドポイント

  • Cache TTL: 24時間(OGPは変更されないため長めに設定可)
  • ユーザーがページ削除時にキャッシュパージ

2. /user/{handle}エンドポイント

  • Cache TTL: 5-10分(投稿一覧は頻繁に更新される)
  • または、初回アクセス時のみRedis、以降はstale-while-revalidate

3. キャッシュパージ機能

  • Firebase Functionsからページ削除時、Workers経由でキャッシュ削除

実装例(キャッシュパージ付き)

// workers/src/index.ts に追加
export default {
    async fetch(request: Request, env: Env) {
        const url = new URL(request.url);
        
        // キャッシュパージエンドポイント(認証必要)
        if (url.pathname.startsWith('/purge/') && request.method === 'POST') {
            const cache = caches.default;
            const keyToPurge = url.pathname.replace('/purge/', '/page/');
            await cache.delete(new Request(keyToPurge, request));
            return new Response(JSON.stringify({ result: 'purged' }), {
                status: 200
            });
        }
        
        // 既存の読み取りロジック
        // ...
    }
}

🚀 段階的実装プラン(推奨)

Phase 1: Cache API実装(即座に効果)

  1. workers/src/function.prod.tsにCache APIを追加
  2. /page/エンドポイントのみキャッシュ(24h TTL)
  3. デプロイして効果測定

実装時間: 1-2時間
期待効果: Redis読み取り 70-90%削減

Phase 2: TTL最適化

  1. /user/エンドポイントにも短いTTLでキャッシュ追加(5-10分)
  2. アクセスログを確認してTTLを調整

実装時間: 30分-1時間
期待効果: Redis読み取り 80-95%削減

Phase 3: 必要に応じてKV移行

  1. 無料枠を超える場合のみKV導入を検討
  2. Firebase Functionsの書き込み先をKVに追加
  3. WorkersをKV優先読み取りに変更

実装時間: 4-6時間
期待効果: Redis読み取り 95%+削減


📈 効果予測

Cache API のみ(案1)

  • 初期実装時間: 1-2時間
  • Redis削減率: 70-90%(アクセスパターン次第)
  • 追加コスト: 0円

Cache + KV(案2+3)

  • 初期実装時間: 4-6時間
  • Redis削減率: 95%+
  • 追加コスト: 無料枠内なら0円

🎯 最終推奨

まずは案1(Cache API)を実装することを推奨します。

理由

  1. 最小変更で即座に効果
  2. 無料で80-90%のRedis読み取りを削減可能
  3. 必要に応じて後からKVを追加できる

実装後の確認

  • Upstashダッシュボードで実際の削減率を確認
  • 無料枠に収まらない場合のみKV導入を検討
  • 段階的アプローチで確実に改善

追加の最適化案

キャッシュ戦略の詳細設定

// TTLを内容に応じて動的に設定
const getCacheTTL = (pathArray: string[]): number => {
    const object = pathArray[1];
    
    switch (object) {
        case 'page':
            // 個別ページは24時間(OGPは不変)
            return 60 * 60 * 24;
        case 'user':
            // ユーザー一覧は10分(更新頻度が高い)
            return 60 * 10;
        default:
            return 60 * 5; // デフォルト5分
    }
};

キャッシュヘッダーの最適化

const headers = {
    'Content-Type': 'application/json',
    'Cache-Control': `public, max-age=${ttl}, s-maxage=${ttl}`,
    'CDN-Cache-Control': `max-age=${ttl}`,
    'Cloudflare-CDN-Cache-Control': `max-age=${ttl}`,
};

エラーハンドリング

try {
    // キャッシュからの読み取り
    let response = await cache.match(cacheKey);
    if (response) {
        return await response.json();
    }
} catch (error) {
    // キャッシュエラー時は通常通りRedisから読み取り
    console.error('Cache error:', error);
}

監視とメトリクス

実装後、以下の指標を監視:

  1. Upstash Redis

    • 日次読み取り回数
    • 無料枠との比較
  2. Cloudflare Workers

    • キャッシュヒット率
    • レスポンスタイム
  3. ユーザー体験

    • ページ読み込み速度
    • エラー率

まとめ

最小の変更で最大の効果を得るため、Phase 1のCache API実装から開始し、効果を測定しながら段階的に最適化していくアプローチを推奨します。

Cloudflare Workers無料利用枠消費削減の対処案

現状分析

現在のアーキテクチャ

[Astro Frontend (Cloudflare Pages)]
         ↓ fetch
[Cloudflare Workers] ← 全てのpageDB読み取りリクエストがここを経由
         ↓
[Upstash Redis]

問題点

  1. Cloudflare Workers無料枠: 1日あたり100,000リクエスト
  2. 呼び出し元:
    • Astroフロントエンド (astro/src/lib/pagedbAPI/getIds.ts, getPage.ts)
    • ユーザーのページ閲覧ごとにWorkers呼び出し
    • OGP一覧表示時に複数リクエスト
  3. 削減の余地: Astro自体がCloudflare上で動作しているため、Workersを経由せずに直接Upstash Redisにアクセス可能

対処案の比較

Workers削減率 実装コスト 追加コスト 備考
1. Astro API Routes統合 100% ⭐ 小 無料 最推奨
2. Cloudflare KV移行 95%+ ⭐⭐ 中 無料枠内 Workersほぼ不要
3. Astro SSG + ISR 90%+ ⭐⭐⭐ 大 無料 静的生成
4. バッチAPI実装 70-80% ⭐⭐ 中 無料 リクエスト数削減
5. キャッシュ統合 60-70% ⭐ 小 無料 plan.mdと併用

✅ 推奨案1: Astro API Routes統合(Workers完全排除)

概要

Cloudflare WorkersをAstro API Routesに統合し、Workersを完全に不要にします。

アーキテクチャ変更

Before:
[Astro Pages] → fetch → [Workers] → [Upstash Redis]

After:
[Astro Pages] → import → [Astro API Routes] → [Upstash Redis]
                                ↓
                        (同一Cloudflare Pages環境内)

メリット

  • Workers呼び出しが100%削減
  • 追加コスト0円(Cloudflare Pagesの無料枠: 100k requests/month + 500k requests/month for functions)
  • レイテンシ削減(同一環境内での処理)
  • CORSの問題が解消
  • 実装コストが最小(既存コードの移行のみ)

デメリット

  • ⚠️ Workers用コードをAstro用に移行する必要
  • ⚠️ Upstash Redis SDKの依存関係を追加

実装手順

Step 1: Astro API Routesの作成

// astro/src/pages/api/pagedb/[...path].ts
import { Redis } from "@upstash/redis/cloudflare";
import type { APIRoute } from 'astro';

export const prerender = false; // SSR mode

export const GET: APIRoute = async ({ params, request }) => {
    const runtime = Astro.locals.runtime;
    
    // Cloudflare環境変数から取得
    const redis = Redis.fromEnv({
        UPSTASH_REDIS_REST_URL: runtime.env.UPSTASH_REDIS_REST_URL,
        UPSTASH_REDIS_REST_TOKEN: runtime.env.UPSTASH_REDIS_REST_TOKEN,
    });

    const pathArray = params.path?.split('/') || [];
    
    try {
        const object = pathArray[0];
        const param = decodeURIComponent(pathArray[1] || '');
        
        switch (object) {
            case 'page': {
                const dataBodyEncoded = await redis.get<string>(param);
                if (dataBodyEncoded !== null) {
                    const dataTyped = JSON.parse(
                        Buffer.from(dataBodyEncoded, "base64").toString()
                    );
                    return new Response(JSON.stringify(dataTyped), {
                        status: 200,
                        headers: { 'Content-Type': 'application/json' }
                    });
                }
                return new Response(
                    JSON.stringify({ error: "No item", message: `${param} not found` }), 
                    { status: 404 }
                );
            }
            
            case 'user': {
                const [_, keys] = await redis.scan(0, { match: `${param}*` });
                return new Response(
                    JSON.stringify({ ids: keys }), 
                    { status: 200, headers: { 'Content-Type': 'application/json' } }
                );
            }
            
            default:
                return new Response(
                    JSON.stringify({ error: "Invalid request" }), 
                    { status: 400 }
                );
        }
    } catch (error) {
        return new Response(
            JSON.stringify({ error: "Server error", message: String(error) }), 
            { status: 500 }
        );
    }
};

Step 2: 環境変数の設定

# astro/.env.production
PUBLIC_GETPAGES_ENDPOINT="/api/pagedb"
# wrangler.toml (Cloudflare Pages設定)
[env.production]
UPSTASH_REDIS_REST_URL = "https://..."
UPSTASH_REDIS_REST_TOKEN = "..."

Step 3: package.jsonに依存関係追加

{
  "dependencies": {
    "@upstash/redis": "^1.28.1"
  }
}

Step 4: 既存のAPI呼び出しコードは変更不要

astro/src/lib/pagedbAPI/getIds.tsgetPage.tsは、エンドポイントURLが環境変数経由で変更されるだけで、コード変更不要。

削減効果

  • Workers呼び出し: 100%削減(0になる)
  • 追加コスト: 0円
  • レイテンシ: 30-50ms改善(Workers経由を排除)

✅ 推奨案2: Cloudflare KV移行(Workers軽量化)

概要

Upstash RedisからCloudflare KVに移行し、Workersの呼び出しを最小化。

アーキテクチャ

[Firebase Functions] → [Cloudflare KV] (書き込み)
                              ↓
[Astro Pages] → [Astro API Routes] → [Cloudflare KV] (読み取り)

メリット

  • ✅ Workers不要(Astro API Routesで完結)
  • ✅ KV無料枠: 100k reads/day(十分)
  • ✅ Upstash Redis完全排除可能
  • ✅ 読み取りレイテンシがさらに低い

デメリット

  • ⚠️ Firebase Functionsからの書き込みロジック変更が必要
  • ⚠️ データ移行が必要

実装概要

KV Namespaceの作成

wrangler kv:namespace create "PAGEDB"

Firebase Functionsからの書き込み

// firebase/functions/src/ogpGenerator/addPagedb.ts
export const addToCloudflareKV = async ({
    keyName, 
    dbData
}: {
    keyName: string,
    dbData: any
}) => {
    const kvWriteUrl = `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/storage/kv/namespaces/${NAMESPACE_ID}/values/${keyName}`;
    
    await fetch(kvWriteUrl, {
        method: 'PUT',
        headers: {
            'Authorization': `Bearer ${CLOUDFLARE_API_TOKEN}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(dbData)
    });
};

Astro API Routesでの読み取り

// astro/src/pages/api/pagedb/[...path].ts
export const GET: APIRoute = async ({ params, locals }) => {
    const kv = locals.runtime.env.PAGEDB_KV;
    const pathArray = params.path?.split('/') || [];
    
    switch (pathArray[0]) {
        case 'page': {
            const data = await kv.get(pathArray[1], { type: 'json' });
            return new Response(JSON.stringify(data), { status: 200 });
        }
        case 'user': {
            const list = await kv.list({ prefix: pathArray[1] });
            const ids = list.keys.map(k => k.name);
            return new Response(JSON.stringify({ ids }), { status: 200 });
        }
    }
};

💡 推奨案3: バッチAPI実装(リクエスト数削減)

概要

複数のページ情報を1回のリクエストで取得できるバッチAPIを実装。

問題の特定

現在、ユーザーのページ一覧取得時:

  1. GET /user/{handle} でID一覧を取得(1リクエスト)
  2. 各ページの詳細は個別に取得する実装も想定される(Nリクエスト)

→ 合計 N+1 リクエスト問題

解決策

// 新規エンドポイント: GET /user/{handle}/pages?full=true
export const GET: APIRoute = async ({ params, url }) => {
    const handle = params.handle;
    const full = url.searchParams.get('full') === 'true';
    
    const redis = Redis.fromEnv(...);
    const [_, keys] = await redis.scan(0, { match: `${handle}*` });
    
    if (!full) {
        // ID一覧のみ
        return new Response(JSON.stringify({ ids: keys }), { status: 200 });
    }
    
    // 全データを一括取得
    const pipeline = redis.pipeline();
    keys.forEach(key => pipeline.get(key));
    const results = await pipeline.exec();
    
    const pages = keys.map((key, i) => ({
        id: key,
        data: results[i] ? JSON.parse(Buffer.from(results[i], 'base64').toString()) : null
    }));
    
    return new Response(JSON.stringify({ pages }), { status: 200 });
};

削減効果

  • リクエスト数: N+1 → 1(90%+削減)

🔄 推奨案4: キャッシュ統合(plan.mdとの併用)

概要

plan.mdのCache API実装に加え、Astro側でもキャッシュを実装。

Astro側のキャッシュ実装

// astro/src/lib/pagedbAPI/getPage.ts
import { getLRU } from '@/utils/lruCache';

const cache = getLRU<pageFetchOutput>(100); // 最大100件キャッシュ
const CACHE_TTL = 60 * 60 * 24 * 1000; // 24時間

export const api = async ({ id }: { id: string }) => {
    // メモリキャッシュ確認
    const cached = cache.get(id);
    if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
        return cached.data;
    }
    
    // API呼び出し
    const url = new URL(object + "/" + encodeURIComponent(id), endpoint_url);
    const data = await fetch(url).then(r => r.json());
    
    // キャッシュに保存
    cache.set(id, { data, timestamp: Date.now() });
    
    return data;
};

削減効果

  • Workers呼び出し: 70-80%削減(セッション内での再利用)

📊 推奨案5: 段階的SSG移行

概要

頻繁にアクセスされるページを静的生成(SSG)に移行。

実装

// astro/src/pages/posts/[handle]/[rkey].astro
export async function getStaticPaths() {
    // ビルド時に人気ページのみ生成
    const popularPages = await getPopularPages(); // 上位100件など
    
    return popularPages.map(page => ({
        params: { handle: page.handle, rkey: page.rkey },
        props: { pageData: page.data }
    }));
}

export const prerender = true;

メリット

  • ✅ 人気ページはWorkers呼び出し不要
  • ✅ レスポンスが最速

デメリット

  • ⚠️ ビルド時間増加
  • ⚠️ 新規ページは動的に対応

🚀 段階的実装プラン(推奨)

Phase 1: Astro API Routes統合(即座に効果)

実装時間: 2-3時間

  1. Astro API Routesを作成(api/pagedb/[...path].ts
  2. Upstash Redis SDKをインストール
  3. 環境変数を設定
  4. デプロイしてWorkers使用量を確認

期待効果:

  • Workers呼び出し: 100%削減
  • 追加コスト: 0円

Phase 2: Cache API統合(plan.mdと併用)

実装時間: 1-2時間

  1. Astro API RoutesにCache APIを追加
  2. TTL戦略を実装(ページ: 24h、一覧: 10m)

期待効果:

  • Upstash Redis読み取り: 80-95%削減
  • レイテンシ: 30-50ms改善

Phase 3: 必要に応じてKV移行

実装時間: 4-6時間

  1. Upstash Redis無料枠を超える場合のみ検討
  2. Cloudflare KVに完全移行
  3. Firebase Functionsの書き込みロジック変更

期待効果:

  • Upstash Redis: 完全排除
  • 月額コスト: $0-0.50

💰 コスト比較

現状(想定)

サービス 使用量 無料枠 超過分
Cloudflare Workers 150k req/day 100k req/day $5/月
Upstash Redis 15k cmd/day 10k cmd/day $0.20/月
合計 - - $5.20/月

案1実装後(Astro API Routes統合)

サービス 使用量 無料枠 超過分
Cloudflare Workers 0 req/day 100k req/day $0/月
Cloudflare Pages Functions 150k req/day 100k req/month + 500k/month $0/月
Upstash Redis 15k cmd/day 10k cmd/day $0.20/月
合計 - - $0.20/月

削減額: $5/月 → 96%コスト削減

案1+2実装後(+ Cache API)

サービス 使用量 無料枠 超過分
Cloudflare Workers 0 req/day - $0/月
Cloudflare Pages Functions 150k req/day 600k req/month $0/月
Upstash Redis 2k cmd/day 10k cmd/day $0/月
合計 - - $0/月

削減額: $5.20/月 → 100%コスト削減


🔧 実装の詳細ガイド

Astro API Routesの完全実装

ファイル構成

astro/src/pages/api/
├── pagedb/
│   └── [...path].ts          # 動的ルート
└── pagedb-batch/
    └── [handle].ts           # バッチAPI(オプション)

完全なコード例

// astro/src/pages/api/pagedb/[...path].ts
import { Redis } from "@upstash/redis/cloudflare";
import type { APIRoute } from 'astro';

export const prerender = false;

// Cache APIラッパー
async function getCachedOrFetch(
    cacheKey: string,
    ttl: number,
    fetchFn: () => Promise<any>
) {
    const cache = await caches.open('pagedb-cache');
    const cached = await cache.match(cacheKey);
    
    if (cached) {
        return cached;
    }
    
    const data = await fetchFn();
    const response = new Response(JSON.stringify(data), {
        headers: {
            'Content-Type': 'application/json',
            'Cache-Control': `public, max-age=${ttl}`,
        }
    });
    
    await cache.put(cacheKey, response.clone());
    return response;
}

export const GET: APIRoute = async ({ params, request, locals }) => {
    const runtime = locals.runtime;
    
    if (!runtime?.env?.UPSTASH_REDIS_REST_URL) {
        return new Response(
            JSON.stringify({ error: "Configuration error" }), 
            { status: 500 }
        );
    }
    
    const redis = Redis.fromEnv({
        UPSTASH_REDIS_REST_URL: runtime.env.UPSTASH_REDIS_REST_URL,
        UPSTASH_REDIS_REST_TOKEN: runtime.env.UPSTASH_REDIS_REST_TOKEN,
    });

    const pathArray = params.path?.split('/') || [];
    const cacheKey = new URL(request.url).toString();
    
    try {
        const object = pathArray[0];
        const param = decodeURIComponent(pathArray[1] || '');
        
        switch (object) {
            case 'page': {
                // 24時間キャッシュ
                return await getCachedOrFetch(
                    cacheKey,
                    60 * 60 * 24,
                    async () => {
                        const dataBodyEncoded = await redis.get<string>(param);
                        if (dataBodyEncoded !== null) {
                            return JSON.parse(
                                Buffer.from(dataBodyEncoded, "base64").toString()
                            );
                        }
                        throw new Error('Page not found');
                    }
                );
            }
            
            case 'user': {
                // 10分キャッシュ
                return await getCachedOrFetch(
                    cacheKey,
                    60 * 10,
                    async () => {
                        const [_, keys] = await redis.scan(0, { 
                            match: `${param}*` 
                        });
                        return { ids: keys };
                    }
                );
            }
            
            default:
                return new Response(
                    JSON.stringify({ error: "Invalid request" }), 
                    { status: 400 }
                );
        }
    } catch (error) {
        return new Response(
            JSON.stringify({ 
                error: error instanceof Error ? error.message : "Unknown error"
            }), 
            { status: error instanceof Error && error.message === 'Page not found' ? 404 : 500 }
        );
    }
};

// OPTIONSリクエスト対応(CORS)
export const OPTIONS: APIRoute = async () => {
    return new Response(null, {
        status: 204,
        headers: {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'GET, OPTIONS',
            'Access-Control-Allow-Headers': 'Content-Type',
        }
    });
};

デプロイ設定

Cloudflare Pages環境変数

Cloudflare Pagesのダッシュボードで設定:

UPSTASH_REDIS_REST_URL = "https://xxxxx.upstash.io"
UPSTASH_REDIS_REST_TOKEN = "AXXXxxxx..."

または wrangler.toml:

[env.production.vars]
UPSTASH_REDIS_REST_URL = "https://xxxxx.upstash.io"
UPSTASH_REDIS_REST_TOKEN = "AXXXxxxx..."

📈 監視とメトリクス

実装後、以下の指標を監視:

Cloudflare Workers

  • リクエスト数: 0になることを確認
  • 削除可能性: Phase 1完了後、Workersプロジェクト自体を削除可能

Cloudflare Pages

  • Functions呼び出し数: Analytics > Functionsで確認
  • 無料枠使用率: 600k/monthに対する使用率

Upstash Redis

  • 日次コマンド数: ダッシュボードで確認
  • キャッシュ効果: Phase 2後の削減率を測定

レスポンスタイム

  • ページ読み込み速度: Chrome DevTools Networkタブ
  • API呼び出し時間: 改善を確認(目標: 30-50ms削減)

⚠️ 注意点とトラブルシューティング

Workers削除前の確認事項

  1. ✅ Astro API Routesが正常動作していることを確認
  2. ✅ 本番環境で1週間テスト運用
  3. ✅ エラーログを確認
  4. ✅ バックアップとしてWorkers設定を保持

ロールバック手順

問題が発生した場合:

  1. 環境変数 PUBLIC_GETPAGES_ENDPOINT を元のWorkers URLに戻す
  2. Astroを再デプロイ
  3. 即座に元の構成に復元可能

よくある問題

問題1: runtime.env が undefined

解決策: astro.config.mjsでCloudflareアダプターが正しく設定されているか確認

import cloudflare from "@astrojs/cloudflare";

export default defineConfig({
  output: "hybrid",
  adapter: cloudflare(),
});

問題2: Buffer is not defined

解決策: Node.js互換性を有効化

# wrangler.toml
compatibility_flags = [ "nodejs_compat" ]

または:

// ブラウザ互換のデコード
const decoded = atob(dataBodyEncoded); // base64デコード

問題3: CORS エラー

解決策: OPTIONSメソッドを実装(上記コード例参照)


🎯 最終推奨

Phase 1(Astro API Routes統合)を最優先で実装してください。

理由

  1. 即座に効果: Workers呼び出しが100%削減
  2. コスト削減: $5/月の削減(96%削減)
  3. 実装時間: 2-3時間で完了
  4. リスク最小: ロールバックが容易
  5. 追加コスト: 完全無料

実装後の次のステップ

  1. 1週間運用して安定性を確認
  2. Cloudflare Workers使用量が0になったことを確認
  3. Workersプロジェクトを削除
  4. Phase 2(Cache API)を実装してさらに最適化

この段階的アプローチにより、リスクを最小限に抑えつつ、確実にコストを削減できます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment