Skip to content

perf: optimizes gpu-prices endpoint#2613

Merged
stalniy merged 2 commits intomainfrom
refactor/gpu-prices
Jan 30, 2026
Merged

perf: optimizes gpu-prices endpoint#2613
stalniy merged 2 commits intomainfrom
refactor/gpu-prices

Conversation

@stalniy
Copy link
Contributor

@stalniy stalniy commented Jan 30, 2026

Why

perf

Summary by CodeRabbit

  • Refactor

    • Optimized GPU pricing calculation logic with improved data retrieval performance using batch processing and streaming approaches.
    • Restructured GPU service module organization for better maintainability.
  • Tests

    • Added comprehensive unit test suite for GPU pricing flow, covering price calculations, bid handling, multi-GPU deployments, provider pricing preferences, and edge cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@stalniy stalniy requested a review from a team as a code owner January 30, 2026 01:55
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

This PR refactors GPU pricing calculations to use async generators and batch processing for deployments, introduces new GPU pricing statistics and model types, and adds comprehensive unit tests for the pricing service with modernized query patterns.

Changes

Cohort / File(s) Summary
Repository Layer Updates
apps/api/src/deployment/repositories/deployment/deployment.repository.ts, apps/api/src/gpu/repositories/day.repository.ts
Reworks findAllWithGpuResources into an async generator yielding deployments in batches of 200; updates getDaysAfter to explicitly return Promise<Day[]> with raw: true option for plain object results.
GPU Pricing Service Refactor
apps/api/src/gpu/services/gpu-price/gpu-price.service.ts
Redesigns GPU price calculation from flat deployment processing to async iteration with chunked aggregation; introduces daysById mapping, unified bid decoding for v1beta4/v1beta5, per-bid GPU extraction, and multi-step pricing using pricing-bot preference logic with fallback to historical-best bid.
Type Definitions
apps/api/src/gpu/types/gpu.type.ts
Introduces GpuPricingStats<T> generic type for currency-specific pricing aggregates and GpuPriceModel type encompassing vendor/model/ram/interface, availability, and pricing data in USD and uakt.
Service Tests
apps/api/src/gpu/services/gpu-price/gpu-price.service.spec.ts
Adds 806-line comprehensive unit test suite covering empty GPU handling, model aggregation, v1beta4/v1beta5 bid variants, denomination filtering, pricing-bot preference, debug output, statistics calculation, and error scenarios.
Import Updates
apps/api/src/gpu/controllers/gpu.controller.ts
Updates GpuPriceService import path from @src/gpu/services/gpu-price.service to @src/gpu/services/gpu-price/gpu-price.service with no functional changes.

Sequence Diagram

sequenceDiagram
    participant Controller as GPU Controller
    participant Service as GpuPriceService
    participant DepRepo as DeploymentRepository
    participant DayRepo as DayRepository
    
    Controller->>Service: getGpuModels()
    
    Service->>DayRepo: getDaysAfter(date)
    DayRepo-->>Service: days[]
    Service->>Service: Create daysById Map
    
    Service->>DepRepo: findAllWithGpuResources(options)
    DepRepo-->>Service: async generator
    
    loop For each batch of deployments
        Service->>Service: Decode bids (v1beta4/v1beta5)
        Service->>Service: Extract GPU features
        Service->>Service: Match bids to GPU models
        Service->>Service: Apply pricing logic<br/>(pricing-bot preference)
        Service->>Service: Compute statistics<br/>(min, max, avg, med, weighted)
        Service->>Service: Accumulate in gpusByModelAndVendor
    end
    
    Service->>Service: Build final result<br/>with aggregated availability
    Service-->>Controller: GpuPriceModel[]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • baktun14
  • ygrishajev

Poem

🐰 Async generators hop through batches bright,
GPU pricing flows with streaming delight,
Bids aggregated, statistics compile,
Pricing-bot hops first with a crypto-smile!
Tests ensure all models priced just right.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: optimizing the gpu-prices endpoint through refactoring and performance improvements across multiple components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/gpu-prices

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/api/src/gpu/repositories/day.repository.ts (1)

1-8: ⚠️ Potential issue | 🟠 Major

Type annotation does not match return value—raw: true returns plain objects, not Day instances.

Line 7 declares Promise<Day[]>, but line 8 uses raw: true, which returns plain attribute objects instead of Day model instances. While the current caller only accesses simple properties (day.id, day.aktPrice), the type annotation is incorrect and will mislead future code that might attempt to use model-specific methods or relationships. Either remove raw: true or update the return type to accurately reflect the plain object structure.

🔧 Suggested fix
-import { Op } from "sequelize";
+import { Op, type Attributes } from "sequelize";
@@
-  async getDaysAfter(date: Date): Promise<Day[]> {
-    return await Day.findAll({ where: { date: { [Op.gte]: date } }, raw: true });
+  async getDaysAfter(date: Date): Promise<Array<Attributes<Day>>> {
+    return Day.findAll({ where: { date: { [Op.gte]: date } }, raw: true });

Also update the caller in gpu-price.service.ts:61 to reflect the correct type: new Map<string, Attributes<Day>>() instead of new Map<string, Day>().

apps/api/src/deployment/repositories/deployment/deployment.repository.ts (1)

109-135: ⚠️ Potential issue | 🟠 Major

Use separate: true for nested hasMany includes to avoid duplicate rows, or manually deduplicate IDs.

The nested include for DeploymentGroup → DeploymentGroupResource with raw: true causes Sequelize to return one row per child combination. A deployment with multiple groups/resources appears duplicated, inflating the IDs array and causing redundant batch queries.

Recommended approaches:

  1. Use separate: true (preferred) — Sequelize fetches children in separate queries instead of joining, avoiding row multiplication:
Suggested change
    const recordsWithIds = await Deployment.findAll({
      attributes: ["id"],
      where: { createdHeight: { [Op.gte]: options.minHeight } },
      include: [
        {
          attributes: [],
          model: DeploymentGroup,
          required: true,
+         separate: true,
          include: [
            {
              attributes: [],
              model: DeploymentGroupResource,
              required: true,
              where: { gpuUnits: 1 }
            }
          ]
        }
      ],
      raw: true
    });
  1. Alternative: Deduplicate manually — If changing the query structure is not feasible, use new Set():
    const ids = [...new Set(recordsWithIds.map(r => r.id))];
🤖 Fix all issues with AI agents
In `@apps/api/src/gpu/services/gpu-price.service.ts`:
- Around line 89-117: The code is using `any` for bid resource parsing (e.g., in
the `decodedBid.resourcesOffer` filter/flatMap/map chains and usages of
`resources.gpu.units.val`, `resources.cpu.units.val`,
`resources.memory.quantity.val`, `resources.storage`), which breaks type safety;
define a typed alias (e.g., `type ResourceOffer =
MsgCreateBidV4["resourcesOffer"][number] |
MsgCreateBidV5["resourcesOffer"][number]`) and cast `decodedBid.resourcesOffer`
to `ResourceOffer[]` once (e.g., `const resourcesOffer: ResourceOffer[] =
decodedBid.resourcesOffer`) then update the filter/flatMap/map callbacks to use
`ResourceOffer` instead of `any`, keeping references to `getGpusFromAttributes`,
and ensure the parsing helpers (`uint8arrayToString`, `parseInt`) operate on the
typed fields `resources.gpu.units.val`, `resources.cpu.units.val`,
`resources.memory.quantity.val`, and `resources.storage` so all four usages are
strongly typed and imported types (`MsgCreateBidV4`, `MsgCreateBidV5`) are
added.

@codecov
Copy link

codecov bot commented Jan 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 50.13%. Comparing base (d80986f) to head (6e74e21).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2613      +/-   ##
==========================================
- Coverage   50.39%   50.13%   -0.26%     
==========================================
  Files        1029     1019      -10     
  Lines       29230    28896     -334     
  Branches     6657     6620      -37     
==========================================
- Hits        14729    14488     -241     
+ Misses      14210    14011     -199     
- Partials      291      397     +106     
Flag Coverage Δ *Carryforward flag
api 78.46% <100.00%> (+0.12%) ⬆️
deploy-web 32.22% <ø> (ø) Carriedforward from d80986f
log-collector ?
notifications 87.94% <ø> (ø) Carriedforward from d80986f
provider-console 81.48% <ø> (ø) Carriedforward from d80986f
provider-proxy 84.35% <ø> (ø) Carriedforward from d80986f

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...t/repositories/deployment/deployment.repository.ts 84.78% <100.00%> (+2.73%) ⬆️
apps/api/src/gpu/controllers/gpu.controller.ts 100.00% <100.00%> (ø)
apps/api/src/gpu/repositories/day.repository.ts 100.00% <100.00%> (ø)
...pi/src/gpu/services/gpu-price/gpu-price.service.ts 97.03% <100.00%> (ø)

... and 47 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@stalniy
Copy link
Contributor Author

stalniy commented Jan 30, 2026

Screenshot 2026-01-30 at 03 13 41

was
Screenshot 2026-01-30 at 03 13 56

baktun14
baktun14 previously approved these changes Jan 30, 2026
@baktun14
Copy link
Contributor

#2587

@stalniy stalniy force-pushed the refactor/gpu-prices branch from af95bee to 6e74e21 Compare January 30, 2026 07:08
@stalniy stalniy changed the title refactor: optimizes gpu-prices endpoint perf: optimizes gpu-prices endpoint Jan 30, 2026
@stalniy stalniy merged commit 4fd2c43 into main Jan 30, 2026
99 of 100 checks passed
@stalniy stalniy deleted the refactor/gpu-prices branch January 30, 2026 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments