Files
cloud-server/ANVIL_IMPLEMENTATION.md
kappa 9f3d3a245a refactor: simplify pricing tables to USD-only
- Remove KRW pricing calculations from all pricing tables
- Simplify pricing table to store only wholesale USD prices
- Simplify anvil_pricing to store only retail USD prices
- Remove KRW environment variables (KRW_EXCHANGE_RATE, KRW_MARGIN_RATE)
- Remove KRW functions from constants.ts
- Update GPU/G8/VPU pricing repositories to match
- Add Anvil tables and repositories for branded product support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 21:16:25 +09:00

8.0 KiB
Raw Blame History

Anvil Product Tables Implementation

Overview

Implemented complete database schema and repositories for Anvil-branded cloud products, including regions, instances, pricing, and transfer pricing.

Implementation Summary

1. Database Migration (004_anvil_tables.sql)

Created 4 new tables with proper indexes, triggers, and foreign key constraints:

anvil_regions

  • Maps Anvil-branded regions to source provider regions
  • Contains 6 initial regions (Tokyo 1-3, Osaka 1-2, Seoul 1)
  • Links to existing regions table via source_region_id

Columns:

  • id, name (unique), display_name, country_code
  • source_provider, source_region_code, source_region_id (FK)
  • active, created_at, updated_at

Initial Data:

anvil-tyo1  → Linode ap-northeast (region_id: 26)
anvil-tyo2  → Linode jp-tyo-3 (region_id: 3)
anvil-tyo3  → Vultr nrt (region_id: 323)
anvil-osa1  → Linode jp-osa (region_id: 13)
anvil-osa2  → Vultr itm (region_id: 314)
anvil-sel1  → Vultr icn (region_id: 313)

anvil_instances

  • Defines Anvil product specifications
  • Supports categories: vm, gpu, g8, vpu
  • GPU-specific fields: gpu_model, gpu_vram_gb

Columns:

  • id, name (unique), display_name, category
  • vcpus, memory_gb, disk_gb
  • transfer_tb, network_gbps
  • gpu_model, gpu_vram_gb (nullable)
  • active, created_at, updated_at

anvil_pricing

  • Retail pricing with cost tracking
  • Auto-calculates KRW prices using exchange rates
  • Links to both Anvil instances/regions and source instances

Columns:

  • id, anvil_instance_id (FK), anvil_region_id (FK)
  • hourly_price, monthly_price (retail USD)
  • hourly_price_krw, monthly_price_krw (retail KRW)
  • cost_hourly, cost_monthly (wholesale USD)
  • source_instance_id (optional FK to source tables)
  • active, created_at, updated_at
  • UNIQUE constraint on (anvil_instance_id, anvil_region_id)

anvil_transfer_pricing

  • Data transfer pricing per region
  • KRW conversion support

Columns:

  • id, anvil_region_id (FK)
  • price_per_gb (USD), price_per_gb_krw (KRW)
  • included_tb (reference only)
  • active, created_at, updated_at
  • UNIQUE constraint on anvil_region_id

2. TypeScript Types (src/types.ts)

Added 8 new type definitions:

AnvilRegion
AnvilInstance
AnvilPricing
AnvilTransferPricing
AnvilRegionInput
AnvilInstanceInput
AnvilPricingInput
AnvilTransferPricingInput

All Input types auto-derive from their base types, excluding id, created_at, and updated_at.

3. Repositories

Created 4 repository classes following existing BaseRepository pattern:

AnvilRegionsRepository

  • findByName(name: string) - Find by Anvil region name
  • findByCountry(countryCode: string) - Get all regions in a country
  • findBySourceRegion(sourceRegionId: number) - Reverse lookup
  • findActive() - Get all active regions
  • updateActive(id: number, active: boolean) - Toggle active status
  • upsertMany(regions: AnvilRegionInput[]) - Bulk insert/update

AnvilInstancesRepository

  • findByName(name: string) - Find by instance name
  • findByCategory(category) - Filter by vm/gpu/g8/vpu
  • findActive(category?) - Get active instances with optional category filter
  • searchByResources(minVcpus?, minMemoryGb?, minDiskGb?, category?) - Resource-based search
  • updateActive(id: number, active: boolean) - Toggle active status
  • upsertMany(instances: AnvilInstanceInput[]) - Bulk insert/update

AnvilPricingRepository

  • findByInstance(anvilInstanceId: number) - All pricing for an instance
  • findByRegion(anvilRegionId: number) - All pricing in a region
  • findByInstanceAndRegion(instanceId, regionId) - Specific pricing record
  • findActive(instanceId?, regionId?) - Active pricing with optional filters
  • searchByPriceRange(minHourly?, maxHourly?, minMonthly?, maxMonthly?) - Price range search
  • updateActive(id: number, active: boolean) - Toggle active status
  • upsertMany(pricing: AnvilPricingInput[]) - Bulk insert/update with auto KRW calculation

AnvilTransferPricingRepository

  • findByRegion(anvilRegionId: number) - Get transfer pricing for a region
  • findActive() - Get all active transfer pricing
  • updateActive(id: number, active: boolean) - Toggle active status
  • upsertMany(pricing: AnvilTransferPricingInput[]) - Bulk insert/update with KRW conversion

4. Repository Factory Updates

Updated RepositoryFactory class to include:

  • anvilRegions: AnvilRegionsRepository
  • anvilInstances: AnvilInstancesRepository
  • anvilPricing: AnvilPricingRepository
  • anvilTransferPricing: AnvilTransferPricingRepository

All repositories use lazy singleton pattern for efficient caching.

5. KRW Pricing Support

Pricing repositories automatically calculate KRW prices using environment variables:

  • KRW_EXCHANGE_RATE - Base USD to KRW conversion rate (default: 1450)
  • KRW_VAT_RATE - VAT multiplier (default: 1.1 for 10% VAT)
  • KRW_MARKUP_RATE - Markup multiplier (default: 1.1 for 10% markup)

KRW prices are auto-calculated during upsertMany() operations.

Database Schema

anvil_regions (1) ──< anvil_pricing (M)
anvil_instances (1) ──< anvil_pricing (M)
anvil_regions (1) ──< anvil_transfer_pricing (1)
regions (1) ──< anvil_regions (M) [source mapping]

Deployment Status

Migration 004 applied to remote database (2026-01-25) 17 queries executed successfully 6 initial regions inserted All 4 tables created with indexes and triggers TypeScript compilation successful Deployed to production (Version: 5905e5df-7265-4872-b05e-e3fe4b7d0619)

Usage Examples

Query Anvil Regions

const repos = new RepositoryFactory(env.DB, env);

// Get all Japan regions
const jpRegions = await repos.anvilRegions.findByCountry('JP');
// Returns: anvil-tyo1, anvil-tyo2, anvil-tyo3, anvil-osa1, anvil-osa2

// Find specific region
const tyo1 = await repos.anvilRegions.findByName('anvil-tyo1');

Create Anvil Instance

await repos.anvilInstances.create({
  name: 'anvil-1g-1c',
  display_name: 'Basic 1GB',
  category: 'vm',
  vcpus: 1,
  memory_gb: 1,
  disk_gb: 25,
  transfer_tb: 1,
  network_gbps: 1,
  gpu_model: null,
  gpu_vram_gb: null,
  active: 1,
});

Set Pricing (Auto KRW Calculation)

await repos.anvilPricing.upsertMany([
  {
    anvil_instance_id: 1,
    anvil_region_id: 1,
    hourly_price: 0.015,
    monthly_price: 10,
    cost_hourly: 0.012,
    cost_monthly: 8,
    source_instance_id: 123,
    active: 1,
  }
]);
// KRW prices auto-calculated:
// hourly_price_krw = 0.015 × 1450 × 1.1 × 1.1 ≈ 26.37
// monthly_price_krw = 10 × 1450 × 1.1 × 1.1 ≈ 17,545

Search by Resources

// Find instances with at least 2 vCPUs and 4GB RAM
const instances = await repos.anvilInstances.searchByResources(
  2,    // minVcpus
  4,    // minMemoryGb
  null, // minDiskGb
  'vm'  // category
);

Files Changed

Created

  • /migrations/004_anvil_tables.sql - Database schema
  • /src/repositories/anvil-regions.ts - Regions repository
  • /src/repositories/anvil-instances.ts - Instances repository
  • /src/repositories/anvil-pricing.ts - Pricing repository
  • /src/repositories/anvil-transfer-pricing.ts - Transfer pricing repository
  • /src/repositories/anvil-regions.test.ts - Unit tests

Modified

  • /src/types.ts - Added 8 new types
  • /src/repositories/index.ts - Added 4 new repository exports and factory getters

Next Steps

To complete the Anvil product implementation:

  1. Populate Instance Data: Create Anvil instance definitions (VM, GPU, G8, VPU tiers)
  2. Set Pricing: Map Anvil instances to source providers and set retail pricing
  3. API Endpoints: Create endpoints for querying Anvil products
  4. Sync Service: Implement sync logic to update costs from source providers
  5. Documentation: Add API documentation for Anvil endpoints

Testing

Basic repository tests created in anvil-regions.test.ts. Additional test coverage recommended for:

  • Instance search and filtering
  • Pricing calculations and KRW conversion
  • Transfer pricing queries
  • Edge cases and error handling