Skip to content

feat(async_search): gen proto#4

Merged
forshev merged 11 commits into
mainfrom
214-async-search-proto
Aug 20, 2025
Merged

feat(async_search): gen proto#4
forshev merged 11 commits into
mainfrom
214-async-search-proto

Conversation

@ssnd
Copy link
Copy Markdown
Contributor

@ssnd ssnd commented Jul 18, 2025

Summary by CodeRabbit

  • New Features

    • New async-search HTTP endpoints: start, fetch, cancel, delete, and list with pagination.
    • List returns per-search metadata (status, timestamps, progress, disk usage) and supports filtering.
  • Improvements

    • Explicit async-search statuses (InProgress/Done/Canceled/Error) and retention control.
    • Fetch responses include original request context, timing, progress, disk-usage, merged aggregation results and improved docs handling.

Comment thread api/seqproxyapi/v1/seq_proxy_api.proto
Comment thread api/storeapi/store_api.proto
@forshev forshev force-pushed the 214-async-search-proto branch from 9b379f5 to c598c4b Compare July 31, 2025 11:27
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 31, 2025

📝 Walkthrough

Walkthrough

Introduce a persistent, quota- and retention-aware async-search subsystem: proto endpoints expanded/renamed (start/fetch/cancel/delete/list), richer request/response schema (retention, status enum, timestamps, progress, disk usage), on-disk QPR encoding/merge, multi-worker processing, maintenance loop, and broad proxy/store/grpc/mapping/marshaler/test updates.

Changes

Cohort / File(s) Change Summary
Protos — Proxy & Store APIs
api/seqproxyapi/v1/seq_proxy_api.proto, api/storeapi/store_api.proto
Pluralized async endpoints; added Delete and List RPCs; changed Fetch/Cancel semantics; added retention/with_docs/order field changes; introduced AsyncSearchStatus enum; expanded Start/Fetch responses with status, timestamps, progress, disk usage, and embedded request/response payloads.
Async search runtime — fracmanager
fracmanager/async_searcher.go, fracmanager/encoding.go, fracmanager/encoding_test.go, fracmanager/async_searcher_test.go
Large refactor to persistent on-disk async lifecycle (.info/.qpr/.mqpr/.tmp), multi-worker processing, maintenance loop (expiration/merge/disk checks), disk quotas (MaxSize/MaxSizePerRequest), versioned QPR codec and tests, new public types/methods (status, list/cancel/delete), and helpers for load/merge/IO.
Config & startup wiring
config/config.go, cmd/seq-db/seq-db.go, storeapi/grpc_v1.go
Added AsyncSearch config fields (MaxTotalSize, MaxSizePerRequest) and mapped them into AsyncSearcherConfig (Workers, MaxSize, MaxSizePerRequest) at startup; MustStartAsync now receives frac list.
Proxy search & ingestion logic
proxy/search/async.go, proxy/search/ingestor.go, proxy/search/mock/store_api_client_mock.go, tests/integration_tests/integration_test.go
Updated async request/response model (Retention, WithDocs, Order relocation), concurrent shard/replica fetch + merge, new GetAsyncSearchesList aggregation, pagination helper moved to package-level, updated mocks and integration tests.
gRPC & API adapters / marshaling
proxyapi/grpc_async_search.go, proxyapi/grpc_v1.go, proxyapi/mock/grpc_v1.go, pkg/seqproxyapi/v1/mappings.go, pkg/storeapi/mappings.go, pkg/seqproxyapi/v1/marshaler.go, pkg/seqproxyapi/v1/marshaler_test.go
Added gRPC handlers and SearchIngestor interface extensions (Fetch now returns DocsIterator; added Cancel/Delete/GetList), bidirectional mappings between proto and internal AsyncSearchStatus, custom JSON marshaling for async types, and related tests.
Store API server/client
storeapi/grpc_async_search.go, storeapi/client.go
Start/Fetch adapted to frac lists and retention/with_docs semantics; Fetch returns richer response (status, timestamps, progress, disk usage, query/interval); added Cancel/Delete/GetList handlers; in-memory client forwards new RPCs.
QPR / seq model
seq/qpr.go
Removed JSON key-based AggregatableSamples serialization and helpers; switched to direct map keying for AggBin; adjusted MergeQPRs allocation guard and removed unused imports.
Utilities & small changes
util/bufferpool.go, frac/meta_data_collector.go, frac/processor/search_params.go, proxyapi/ingestor.go
Added BufferPool wrapper for bytespool.Buffer; minor comment rename; added json:"-" tag to SearchParams.AST; added Unmarshal to humanReadableMarshaler.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • dkharms

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 214-async-search-proto

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@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: 0

♻️ Duplicate comments (1)
api/seqproxyapi/v1/seq_proxy_api.proto (1)

269-280: Add AsyncSearchStatusPending to reflect queued searches
[dKharms already raised the same point]

🧹 Nitpick comments (4)
api/storeapi/store_api.proto (2)

160-165: Consider adding a PENDING/QUEUED state to AsyncSearchStatus

The lifecycle currently jumps from InProgressDone/Cancelled/Error. A distinct Pending (or Queued) enum value helps front-ends distinguish “accepted but not started” from “actively running”. This was requested in previous discussions inside the proxy API file; mirroring it here keeps both layers symmetrical.


167-179: Progress metrics diverge from proxy API — unify to avoid translation glitches

Store layer returns fracs_done / fracs_queue as uint64, whereas the proxy layer (see seq_proxy_api.proto Lines 288-290) exposes a single double progress. The gateway now needs bespoke conversion logic and loses precision on large totals. Aligning on one representation (e.g., double progress or {done,total} ints) will simplify marshaling and reduce the chance of rounding/overflow bugs.

api/seqproxyapi/v1/seq_proxy_api.proto (2)

282-292: Mismatch with store-layer progress counters

Proxy response exposes double progress, whereas the store response uses integer fracs_done / fracs_queue. Decide on a single format (preferably percentage as double progress for human-readability) or document the conversion rules in the gateway.


237-250: Retention is mandatory but undocumented default — clarify or make optional

retention is required (field 1). If a client omits it, should the request be rejected or should the server fall back to a sensible default (e.g., 24 h)? Either document the default explicitly in comments or mark the field optional to convey that the server may apply a policy value.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf932a2 and c598c4b.

⛔ Files ignored due to path filters (6)
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.go is excluded by !**/*.pb.go
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/seqproxyapi/v1/seq_proxy_api_vtproto.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/storeapi/store_api_vtproto.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (2)
  • api/seqproxyapi/v1/seq_proxy_api.proto (4 hunks)
  • api/storeapi/store_api.proto (2 hunks)
🔇 Additional comments (1)
api/storeapi/store_api.proto (1)

140-149: Consider making search_id server-generated to avoid collisions
The search_id field is currently required in StartAsyncSearchRequest (api/storeapi/store_api.proto:141) and directly drives request routing and storage in:

  • storeapi/grpc_async_search.go (r.SearchId)
  • storeapi/client.go (inMemoryAPIClient) and its mocks (store_api_client_mock.go)
  • proxy HTTP paths in seq_proxy_api.proto and generated handlers (/async-searches/{search_id})

Switching to a server-generated ID will be a breaking API change. You’ll need to:
• Return the new ID in StartAsyncSearchResponse
• Update all client, mock, proxy and in-memory implementations to consume the returned ID
• Adjust gRPC and HTTP routing to use the generated ID rather than expect it on the request

Please review every usage of search_id across the codebase before proceeding with this refactor.

@forshev forshev force-pushed the 214-async-search-proto branch from c598c4b to 7168308 Compare August 1, 2025 10:38
Copy link
Copy Markdown

@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: 2

♻️ Duplicate comments (2)
api/storeapi/store_api.proto (1)

160-165: Missing AsyncSearchStatusPending – already raised earlier

Same feedback as in the proxy schema: a Pending state can be handy for queued tasks.

api/seqproxyapi/v1/seq_proxy_api.proto (1)

269-280: Pending status again

See earlier remark – a Pending enum could future-proof scheduling.

🧹 Nitpick comments (7)
api/storeapi/store_api.proto (4)

20-23: Prefer re-using google.protobuf.Empty for empty responses

CancelAsyncSearchResponse and DeleteAsyncSearchResponse carry no payload. You could simplify the API surface by re-using google.protobuf.Empty instead of introducing two extra empty messages.

-  rpc CancelAsyncSearch(CancelAsyncSearchRequest) returns (CancelAsyncSearchResponse) {}
-  rpc DeleteAsyncSearch(DeleteAsyncSearchRequest) returns (DeleteAsyncSearchResponse) {}
+  rpc CancelAsyncSearch(CancelAsyncSearchRequest) returns (google.protobuf.Empty) {}
+  rpc DeleteAsyncSearch(DeleteAsyncSearchRequest) returns (google.protobuf.Empty) {}

153-158: Document default pagination behaviour

size/offset became required ints but no defaults or range checks are described.
Please add a short comment (or google.api.field_behavior) clarifying defaults and max limits to avoid ambiguous implementations.


167-179: Progress terminology differs from proxy API

Store API exports fracs_done / fracs_queue as uint64 counters, whereas the proxy API exposes a single progress double in [0,1].
Down-stream services now need bespoke conversion logic. Consider aligning on one representation (preferably a double 0-1 for simplicity).


181-192: Consistency: empty request/response names add noise

CancelAsyncSearchRequest / DeleteAsyncSearchRequest only wrap search_id.
If you keep dedicated empty responses, consider inlining the ID into the RPC path and dropping the wrappers, e.g.:

rpc CancelAsyncSearch(string) returns (google.protobuf.Empty);

…or keep the wrapper but at least share a single SearchId message across RPCs.

api/seqproxyapi/v1/seq_proxy_api.proto (3)

238-250: with_docs flag: highlight storage impact

Great that the comment warns about disk usage.
Consider adding a concrete soft/hard limit or quota mechanism to prevent unbounded growth.


282-292: Progress granularity

double progress (0-1) is straightforward. Make clear whether 1.0 means all shards successful or any terminal state. Add a note.


300-304: Empty request wrapper

Same as store API: consider a shared SearchId message or direct path param without wrapper.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c598c4b and 7168308.

⛔ Files ignored due to path filters (6)
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.go is excluded by !**/*.pb.go
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/seqproxyapi/v1/seq_proxy_api_vtproto.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/storeapi/store_api_vtproto.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (2)
  • api/seqproxyapi/v1/seq_proxy_api.proto (4 hunks)
  • api/storeapi/store_api.proto (2 hunks)
🔇 Additional comments (5)
api/seqproxyapi/v1/seq_proxy_api.proto (5)

79-82: Pluralised endpoint looks good

Switching to /async-searches aligns with REST conventions and avoids a future breaking change.


88-90: GET on item resource is clear – nice

Fetching via GET /async-searches/{search_id} matches HTTP semantics.


95-97: Why POST for cancel instead of DELETE?

Cancelling is state-destructive and idempotent → DELETE /async-searches/{id} would be the canonical verb.
Any strong reason to use POST …/cancel? If not, consider reverting to DELETE.


100-104: HTTP mapping for delete lacks body: "*" – good

No body is expected and the verb is correct. LGTM.


253-255: Response now returns search_id – consistent and user-friendly

👍

Comment thread api/seqproxyapi/v1/seq_proxy_api.proto
Comment thread api/storeapi/store_api.proto
@ssnd ssnd added this to the v0.59.0 milestone Aug 1, 2025
@forshev forshev force-pushed the 214-async-search-proto branch from 7168308 to 52f2e68 Compare August 13, 2025 13:08
@forshev forshev changed the title Gen proto feat(async_search): gen proto Aug 13, 2025
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Aug 20, 2025

Codecov Report

❌ Patch coverage is 61.14215% with 626 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.34%. Comparing base (0b406d7) to head (3023ae8).

Files with missing lines Patch % Lines
fracmanager/async_searcher.go 52.79% 214 Missing and 48 partials ⚠️
proxyapi/grpc_async_search.go 0.00% 115 Missing ⚠️
fracmanager/encoding.go 74.94% 74 Missing and 36 partials ⚠️
proxy/search/async.go 76.10% 57 Missing and 19 partials ⚠️
pkg/seqproxyapi/v1/mappings.go 19.35% 25 Missing ⚠️
storeapi/grpc_async_search.go 83.13% 10 Missing and 4 partials ⚠️
pkg/storeapi/mappings.go 61.53% 6 Missing and 4 partials ⚠️
storeapi/client.go 0.00% 6 Missing ⚠️
cmd/seq-db/seq-db.go 0.00% 4 Missing ⚠️
pkg/seqproxyapi/v1/marshaler.go 88.88% 2 Missing ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main       #4      +/-   ##
==========================================
- Coverage   73.44%   72.34%   -1.11%     
==========================================
  Files         193      195       +2     
  Lines       16015    17350    +1335     
==========================================
+ Hits        11762    12551     +789     
- Misses       3662     4115     +453     
- Partials      591      684      +93     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@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: 11

Caution

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

⚠️ Outside diff range comments (1)
storeapi/grpc_v1.go (1)

24-26: Type mismatch: passing storeapi.MappingProvider to fracmanager.MustStartAsync which expects fracmanager.MappingProvider

These are distinct interface types; this won’t compile. Use a type alias or pass the fracmanager interface explicitly.

Minimal, future-proof fix via alias:

- type MappingProvider interface {
- 	GetMapping() seq.Mapping
- }
+// Re-export fracmanager.MappingProvider to avoid interface drift and enable direct use.
+type MappingProvider = fracmanager.MappingProvider

Alternatively, do a type assertion at the call site (less ideal due to runtime check):

- asyncSearcher: fracmanager.MustStartAsync(cfg.Search.Async, mappingProvider, fracManager.GetAllFracs()),
+ asyncSearcher: fracmanager.MustStartAsync(cfg.Search.Async, mappingProvider.(fracmanager.MappingProvider), fracManager.GetAllFracs()),

Also ensure asyncSearcher is stopped on server shutdown if AsyncSearcher exposes Stop/Close.

Also applies to: 114-115

♻️ Duplicate comments (4)
api/storeapi/store_api.proto (1)

142-151: Asymmetry: client-supplied search_id; empty response — prefer server-generated IDs for safety and consistency

This repeats prior feedback: generating IDs server-side avoids collisions/guessing and aligns with the proxy API.

Apply this proto change to shift ID generation to the server:

message StartAsyncSearchRequest {
-  string search_id = 1;
   google.protobuf.Duration retention = 2;
   string query = 3;
   int64 from = 4;
   int64 to = 5;
   repeated AggQuery aggs = 6;
   int64 histogram_interval = 7;
   bool with_docs = 8;
}

-message StartAsyncSearchResponse {}
+message StartAsyncSearchResponse {
+  string search_id = 1;
+}
proxy/search/async.go (1)

423-436: Fix the status weight mapping issue.

Same issue as in fracmanager/encoding.go - the slice is being used incorrectly as a map.

api/seqproxyapi/v1/seq_proxy_api.proto (2)

278-289: Consider adding AsyncSearchStatusPending status.

As suggested in the past review comment, a pending status could be useful for future scheduler implementations.


268-276: Validate pagination parameters when with_docs is false.

The comments indicate these parameters are ignored when with_docs is false, but there's no validation to reject non-zero values. This could confuse clients.

🧹 Nitpick comments (19)
config/config.go (1)

218-222: Add a simple invariant check between MaxTotalSize and MaxSizePerRequest

Nice additions. Consider validating these two to avoid footguns in prod configs (e.g., per-request limit greater than total budget).

Example (place near the other computed defaults in Parse):

// After setting AsyncSearch defaults
if c.AsyncSearch.MaxSizePerRequest > c.AsyncSearch.MaxTotalSize {
    return Config{}, fmt.Errorf("async_search.max_size_per_request (%s) must be <= async_search.max_total_size (%s)",
        units.Base2Bytes(c.AsyncSearch.MaxSizePerRequest), units.Base2Bytes(c.AsyncSearch.MaxTotalSize))
}
api/storeapi/store_api.proto (4)

144-151: Type inconsistency: from/to are int64 here but Timestamps in responses

Start request uses int64, while FetchAsyncSearchResultResponse and AsyncSearchesListItem use google.protobuf.Timestamp for from/to. Consider unifying to Timestamp for symmetry and easier client handling.


157-160: size/offset downgraded to int32 — confirm limits or use int64 for consistency

Other requests use int64 for size/offset. If you don’t have a strict upper bound, prefer int64 to avoid accidental truncation.

Possible tweak:

message FetchAsyncSearchResultRequest {
  string search_id = 1;
- int32 size = 2;
- int32 offset = 3;
+ int64 size = 2;
+ int64 offset = 3;
  Order order = 4;
}

162-167: Enum naming style deviates from file conventions

Elsewhere enums use SCREAMING_SNAKE_CASE (e.g., NO_ERROR). Consider switching to ASYNC_SEARCH_STATUS_IN_PROGRESS, etc., for consistency. Not blocking.


210-228: Repeat: remove ‘optional’ on message Timestamp; consider unifying types

Same ‘optional’ concern for canceled_at here. Also, consider aligning from/to types with Start request.

Apply:

message AsyncSearchesListItem {
  string search_id = 1;
  AsyncSearchStatus status = 2;
  google.protobuf.Timestamp started_at = 3;
  google.protobuf.Timestamp expires_at = 4;
- optional google.protobuf.Timestamp canceled_at = 5;
+ google.protobuf.Timestamp canceled_at = 5;
  uint64 fracs_done = 6;
  uint64 fracs_queue = 7;
  uint64 disk_usage = 8;

  repeated AggQuery aggs = 9;
  int64 histogram_interval = 10;

  string query = 11;
  google.protobuf.Timestamp from = 12;
  google.protobuf.Timestamp to = 13;
  google.protobuf.Duration retention = 14;
  bool with_docs = 15;
}
proxy/search/ingestor.go (1)

197-210: Minor: avoid keeping backing array when offset exceeds length

ids = ids[:0] retains capacity. Returning nil helps GC and communicates emptiness more clearly.

 func paginateIDs(ids seq.IDSources, offset, size int) (seq.IDSources, int) {
   if len(ids) > offset {
     ids = ids[offset:]
   } else {
-    ids = ids[:0]
+    ids = nil
   }

   if len(ids) > size {
     ids = ids[:size]
   } else {
     size = len(ids)
   }
   return ids, size
 }
util/bufferpool.go (1)

13-24: Add Pool.New factory and simplify Get; optionally expose a constructor

Using sync.Pool.New avoids the nil-branch and centralizes allocation.

 type BufferPool struct {
 	pool sync.Pool
 }
 
-func (q *BufferPool) Get() *bytespool.Buffer {
-	v := q.pool.Get()
-	if v != nil {
-		return v.(*bytespool.Buffer)
-	}
-	return new(bytespool.Buffer)
-}
+func NewBufferPool() *BufferPool {
+	return &BufferPool{
+		pool: sync.Pool{
+			New: func() any { return new(bytespool.Buffer) },
+		},
+	}
+}
+
+func (q *BufferPool) Get() *bytespool.Buffer {
+	return q.pool.Get().(*bytespool.Buffer)
+}
 
 func (q *BufferPool) Put(b *bytespool.Buffer) {
 	b.Reset()
 	q.pool.Put(b)
 }
pkg/seqproxyapi/v1/marshaler_test.go (1)

102-146: Strengthen JSON comparison to avoid brittle failures due to field order

Relying on exact string equality for complex JSON makes the test fragile. Use JSONEq to compare semantically.

-    r.Equal(expected, string(raw))
+    require.JSONEq(t, expected, string(raw))
fracmanager/async_searcher_test.go (1)

49-63: Ensure AsyncSearcher is shut down to avoid goroutine leaks

MustStartAsync spawns a maintenance goroutine; this test waits only for processWg. If AsyncSearcher exposes a shutdown/close method, invoke it via t.Cleanup to prevent leaks.

fracmanager/encoding_test.go (1)

88-92: Make randomized heavy loop test-friendly under -short

Running up to 100 iterations with up to ~8K entries can be slow. Respect testing.Short() to keep CI fast.

-	for i := 0; i < 100; i++ {
+	n := 100
+	if testing.Short() {
+		n = 5
+	}
+	for i := 0; i < n; i++ {
 		r := rand.N(8)
 		qpr := getRandomQPR(r * 1024)
 		test(qpr)
 	}
pkg/seqproxyapi/v1/marshaler.go (1)

231-237: Inconsistent wire format: DiskUsage quoted here, numeric elsewhere.

FetchAsyncSearchResultResponse encodes disk_usage as a JSON string, while AsyncSearchesListItem (and most protojson outputs) encode it as a number. This asymmetry is likely to surprise clients.

Consider making disk_usage consistently numeric across endpoints:

- DiskUsage: json.RawMessage(strconv.Quote(strconv.FormatUint(r.DiskUsage, 10))),
+ DiskUsage: json.RawMessage(strconv.FormatUint(r.DiskUsage, 10)),

Alternatively, switch AsyncSearchesListItem.MarshalJSON to the same string encoding if that’s the intended contract.

pkg/seqproxyapi/v1/mappings.go (2)

86-91: Add lower-bound validation for robustness.

Other converters in this file (e.g., ToAggFunc) also check for negative values. Mirror that here to avoid panics on malformed inputs.

-func (s AsyncSearchStatus) ToAsyncSearchStatus() (fracmanager.AsyncSearchStatus, error) {
-  if int(s) >= len(statusMappingsPb) {
+func (s AsyncSearchStatus) ToAsyncSearchStatus() (fracmanager.AsyncSearchStatus, error) {
+  if s < 0 || int(s) >= len(statusMappingsPb) {
     return 0, fmt.Errorf("unknown status")
   }
   return statusMappingsPb[s], nil
 }

116-129: String-to-status map looks fine; consider documenting accepted values.

The accepted keys are the exact proto names. A short comment noting case-sensitivity and expected values helps avoid misuse.

tests/integration_tests/integration_test.go (1)

2021-2022: Fragile equality on time-bearing request structs.

r.Equal(startReq, fresp.Request) may intermittently fail due to monotonic clock bits in time.Time. Prefer field-wise comparison and use Equal/WithinDuration for From/To.

- r.Equal(startReq, fresp.Request)
+ r.Equal(startReq.Query, fresp.Request.Query)
+ r.Equal(startReq.Retention, fresp.Request.Retention)
+ r.Equal(startReq.WithDocs, fresp.Request.WithDocs)
+ r.Equal(startReq.Aggregations, fresp.Request.Aggregations)
+ r.Equal(startReq.HistogramInterval, fresp.Request.HistogramInterval)
+ r.Truef(fresp.Request.From.Equal(startReq.From) && fresp.Request.To.Equal(startReq.To),
+   "request From/To differ: from=%v vs %v; to=%v vs %v",
+   startReq.From, fresp.Request.From, startReq.To, fresp.Request.To)
storeapi/grpc_async_search.go (3)

18-31: Clarify intent: WithTotal derived from WithDocs may be surprising.

Coupling WithTotal to WithDocs is non-obvious and possibly costly (computing totals is expensive). If the goal is only to persist docs for later fetch, consider decoupling these flags and document the trade-offs.

Also applies to: 32-41


57-66: Validate pagination inputs and guard against overflow.

Negative size/offset or overflow in size+offset can lead to incorrect limits. Fail fast with InvalidArgument and compute limit safely.

 func (g *GrpcV1) FetchAsyncSearchResult(
   _ context.Context,
   r *storeapi.FetchAsyncSearchResultRequest,
 ) (*storeapi.FetchAsyncSearchResultResponse, error) {
+  if r.Size < 0 || r.Offset < 0 {
+    return nil, status.Error(codes.InvalidArgument, "size and offset must be non-negative")
+  }
+  // Compute safe limit for pagination
+  limit := int(r.Size) + int(r.Offset)
+  if limit < 0 {
+    limit = math.MaxInt // overflow guard
+  }
   fr, exists := g.asyncSearcher.FetchSearchResult(fracmanager.FetchSearchResultRequest{
     ID:    r.SearchId,
-    Limit: int(r.Size + r.Offset),
+    Limit: limit,
     Order: r.Order.MustDocsOrder(),
   })

112-130: GetAsyncSearchesList: LGTM; minor nit on panic safety.

MustAsyncSearchStatus will panic on unknown values. Consider using the error-returning variant and mapping to InvalidArgument instead.

proxyapi/grpc_async_search.go (1)

155-166: Consider caching the conversion logic.

The makeProtoRequestAggregations function creates a new slice on every call. Since aggregation definitions are likely to be reused, consider whether caching could be beneficial for performance.

proxy/search/async.go (1)

54-55: Verify the TODO comment about WithDocs.

The comment indicates that WithDocs is hardcoded to false despite being passed in the request. This seems like a significant limitation that should be tracked.

Would you like me to create an issue to track enabling WithDocs after implementing async searches' QPRs merging in batches?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 52f2e68 and 03c2206.

⛔ Files ignored due to path filters (6)
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.go is excluded by !**/*.pb.go
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/seqproxyapi/v1/seq_proxy_api_vtproto.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/storeapi/store_api_vtproto.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (27)
  • api/seqproxyapi/v1/seq_proxy_api.proto (4 hunks)
  • api/storeapi/store_api.proto (2 hunks)
  • cmd/seq-db/seq-db.go (1 hunks)
  • config/config.go (1 hunks)
  • frac/meta_data_collector.go (1 hunks)
  • frac/processor/search_params.go (1 hunks)
  • fracmanager/async_searcher.go (6 hunks)
  • fracmanager/async_searcher_test.go (1 hunks)
  • fracmanager/encoding.go (1 hunks)
  • fracmanager/encoding_test.go (1 hunks)
  • pkg/seqproxyapi/v1/mappings.go (2 hunks)
  • pkg/seqproxyapi/v1/marshaler.go (1 hunks)
  • pkg/seqproxyapi/v1/marshaler_test.go (1 hunks)
  • pkg/storeapi/mappings.go (2 hunks)
  • proxy/search/async.go (4 hunks)
  • proxy/search/ingestor.go (2 hunks)
  • proxy/search/mock/store_api_client_mock.go (2 hunks)
  • proxyapi/grpc_async_search.go (3 hunks)
  • proxyapi/grpc_v1.go (1 hunks)
  • proxyapi/ingestor.go (1 hunks)
  • proxyapi/mock/grpc_v1.go (3 hunks)
  • seq/qpr.go (1 hunks)
  • storeapi/client.go (1 hunks)
  • storeapi/grpc_async_search.go (3 hunks)
  • storeapi/grpc_v1.go (1 hunks)
  • tests/integration_tests/integration_test.go (4 hunks)
  • util/bufferpool.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frac/meta_data_collector.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: forshev
PR: ozontech/seq-db#3
File: api/seqproxyapi/v1/seq_proxy_api.proto:88-90
Timestamp: 2025-08-05T10:52:04.737Z
Learning: In the seq-db project, the FetchAsyncSearchResult RPC method in api/seqproxyapi/v1/seq_proxy_api.proto was intentionally changed from HTTP GET to POST during development. This breaking change is acceptable because the async search feature is under active development and not yet in production use.
📚 Learning: 2025-08-05T10:52:04.737Z
Learnt from: forshev
PR: ozontech/seq-db#3
File: api/seqproxyapi/v1/seq_proxy_api.proto:88-90
Timestamp: 2025-08-05T10:52:04.737Z
Learning: In the seq-db project, the FetchAsyncSearchResult RPC method in api/seqproxyapi/v1/seq_proxy_api.proto was intentionally changed from HTTP GET to POST during development. This breaking change is acceptable because the async search feature is under active development and not yet in production use.

Applied to files:

  • proxyapi/grpc_v1.go
  • pkg/seqproxyapi/v1/marshaler.go
  • api/seqproxyapi/v1/seq_proxy_api.proto
  • storeapi/grpc_async_search.go
  • api/storeapi/store_api.proto
  • fracmanager/async_searcher.go
  • proxy/search/async.go
  • proxyapi/grpc_async_search.go
📚 Learning: 2025-08-05T10:51:10.548Z
Learnt from: forshev
PR: ozontech/seq-db#3
File: proxy/search/async.go:155-171
Timestamp: 2025-08-05T10:51:10.548Z
Learning: In proxy/search/async.go, the FetchAsyncSearchResult method uses an `anyResponse` variable to track if any successful responses were received from any shard. If all shards return NotFound errors, the function properly returns a NotFound status error, so individual shard-level response tracking is not necessary.

Applied to files:

  • pkg/seqproxyapi/v1/marshaler_test.go
  • api/seqproxyapi/v1/seq_proxy_api.proto
  • storeapi/grpc_async_search.go
  • api/storeapi/store_api.proto
  • proxy/search/async.go
  • tests/integration_tests/integration_test.go
  • proxyapi/grpc_async_search.go
🧬 Code Graph Analysis (20)
util/bufferpool.go (1)
bytespool/bytespool.go (2)
  • Pool (87-98)
  • Buffer (62-64)
fracmanager/async_searcher_test.go (5)
frac/fraction.go (2)
  • Fraction (18-24)
  • DataProvider (13-16)
seq/qpr.go (1)
  • QPR (68-74)
frac/processor/search_params.go (1)
  • SearchParams (16-28)
fracmanager/async_searcher.go (3)
  • AsyncSearcherConfig (87-93)
  • MustStartAsync (95-130)
  • AsyncSearchRequest (132-137)
mappingprovider/mapping_provider.go (1)
  • WithMapping (26-31)
proxyapi/grpc_v1.go (3)
proxy/search/async.go (4)
  • FetchAsyncSearchResultRequest (80-85)
  • FetchAsyncSearchResultResponse (87-101)
  • GetAsyncSearchesListRequest (103-108)
  • AsyncSearchesListItem (110-122)
pkg/seqproxyapi/v1/seq_proxy_api.pb.go (12)
  • FetchAsyncSearchResultRequest (1171-1184)
  • FetchAsyncSearchResultRequest (1197-1197)
  • FetchAsyncSearchResultRequest (1212-1214)
  • FetchAsyncSearchResultResponse (1244-1258)
  • FetchAsyncSearchResultResponse (1271-1271)
  • FetchAsyncSearchResultResponse (1286-1288)
  • GetAsyncSearchesListRequest (1506-1517)
  • GetAsyncSearchesListRequest (1530-1530)
  • GetAsyncSearchesListRequest (1545-1547)
  • AsyncSearchesListItem (1621-1635)
  • AsyncSearchesListItem (1648-1648)
  • AsyncSearchesListItem (1663-1665)
fracmanager/async_searcher.go (2)
  • GetAsyncSearchesListRequest (927-930)
  • AsyncSearchesListItem (932-952)
storeapi/grpc_v1.go (1)
fracmanager/async_searcher.go (1)
  • MustStartAsync (95-130)
proxy/search/ingestor.go (1)
seq/qpr.go (1)
  • IDSources (41-41)
pkg/seqproxyapi/v1/marshaler_test.go (1)
pkg/seqproxyapi/v1/seq_proxy_api.pb.go (21)
  • FetchAsyncSearchResultResponse (1244-1258)
  • FetchAsyncSearchResultResponse (1271-1271)
  • FetchAsyncSearchResultResponse (1286-1288)
  • StartAsyncSearchRequest (1043-1059)
  • StartAsyncSearchRequest (1072-1072)
  • StartAsyncSearchRequest (1087-1089)
  • SearchQuery (460-468)
  • SearchQuery (481-481)
  • SearchQuery (496-498)
  • AggQuery (529-538)
  • AggQuery (551-551)
  • AggQuery (566-568)
  • ComplexSearchResponse (949-961)
  • ComplexSearchResponse (974-974)
  • ComplexSearchResponse (989-991)
  • Document (301-308)
  • Document (321-321)
  • Document (336-338)
  • Histogram (415-420)
  • Histogram (433-433)
  • Histogram (448-450)
fracmanager/encoding_test.go (2)
seq/qpr.go (8)
  • QPR (68-74)
  • ErrorSource (61-64)
  • IDSources (41-41)
  • AggregatableSamples (113-116)
  • AggBin (108-111)
  • SamplesContainer (256-267)
  • IDSource (31-35)
  • NewSamplesContainers (269-277)
seq/seq.go (4)
  • MID (17-17)
  • ID (12-15)
  • RID (18-18)
  • NewID (115-119)
pkg/seqproxyapi/v1/marshaler.go (1)
pkg/seqproxyapi/v1/seq_proxy_api.pb.go (9)
  • StartAsyncSearchRequest (1043-1059)
  • StartAsyncSearchRequest (1072-1072)
  • StartAsyncSearchRequest (1087-1089)
  • FetchAsyncSearchResultResponse (1244-1258)
  • FetchAsyncSearchResultResponse (1271-1271)
  • FetchAsyncSearchResultResponse (1286-1288)
  • AsyncSearchesListItem (1621-1635)
  • AsyncSearchesListItem (1648-1648)
  • AsyncSearchesListItem (1663-1665)
storeapi/client.go (2)
pkg/storeapi/store_api.pb.go (18)
  • CancelAsyncSearchRequest (1076-1081)
  • CancelAsyncSearchRequest (1094-1094)
  • CancelAsyncSearchRequest (1109-1111)
  • CancelAsyncSearchResponse (1120-1124)
  • CancelAsyncSearchResponse (1137-1137)
  • CancelAsyncSearchResponse (1152-1154)
  • DeleteAsyncSearchRequest (1156-1161)
  • DeleteAsyncSearchRequest (1174-1174)
  • DeleteAsyncSearchRequest (1189-1191)
  • DeleteAsyncSearchResponse (1200-1204)
  • DeleteAsyncSearchResponse (1217-1217)
  • DeleteAsyncSearchResponse (1232-1234)
  • GetAsyncSearchesListRequest (1236-1242)
  • GetAsyncSearchesListRequest (1255-1255)
  • GetAsyncSearchesListRequest (1270-1272)
  • GetAsyncSearchesListResponse (1288-1293)
  • GetAsyncSearchesListResponse (1306-1306)
  • GetAsyncSearchesListResponse (1321-1323)
storeapi/grpc_v1.go (1)
  • GrpcV1 (83-98)
frac/processor/search_params.go (1)
parser/ast_node.go (1)
  • ASTNode (8-11)
proxy/search/mock/store_api_client_mock.go (2)
pkg/storeapi/store_api.pb.go (18)
  • CancelAsyncSearchRequest (1076-1081)
  • CancelAsyncSearchRequest (1094-1094)
  • CancelAsyncSearchRequest (1109-1111)
  • CancelAsyncSearchResponse (1120-1124)
  • CancelAsyncSearchResponse (1137-1137)
  • CancelAsyncSearchResponse (1152-1154)
  • DeleteAsyncSearchRequest (1156-1161)
  • DeleteAsyncSearchRequest (1174-1174)
  • DeleteAsyncSearchRequest (1189-1191)
  • DeleteAsyncSearchResponse (1200-1204)
  • DeleteAsyncSearchResponse (1217-1217)
  • DeleteAsyncSearchResponse (1232-1234)
  • GetAsyncSearchesListRequest (1236-1242)
  • GetAsyncSearchesListRequest (1255-1255)
  • GetAsyncSearchesListRequest (1270-1272)
  • GetAsyncSearchesListResponse (1288-1293)
  • GetAsyncSearchesListResponse (1306-1306)
  • GetAsyncSearchesListResponse (1321-1323)
proxy/search/async.go (1)
  • GetAsyncSearchesListRequest (103-108)
fracmanager/encoding.go (3)
seq/qpr.go (7)
  • QPR (68-74)
  • IDSources (41-41)
  • IDSource (31-35)
  • AggregatableSamples (113-116)
  • AggBin (108-111)
  • SamplesContainer (256-267)
  • ErrorSource (61-64)
util/bufferpool.go (1)
  • BufferPool (9-11)
seq/seq.go (3)
  • MID (17-17)
  • ID (12-15)
  • RID (18-18)
pkg/storeapi/mappings.go (3)
fracmanager/async_searcher.go (5)
  • AsyncSearchStatus (627-627)
  • AsyncSearchStatusDone (630-630)
  • AsyncSearchStatusInProgress (631-631)
  • AsyncSearchStatusError (632-632)
  • AsyncSearchStatusCanceled (633-633)
pkg/storeapi/store_api.pb.go (8)
  • AsyncSearchStatus (186-186)
  • AsyncSearchStatus (221-223)
  • AsyncSearchStatus (225-227)
  • AsyncSearchStatus (234-236)
  • AsyncSearchStatus_AsyncSearchStatusDone (190-190)
  • AsyncSearchStatus_AsyncSearchStatusInProgress (189-189)
  • AsyncSearchStatus_AsyncSearchStatusError (192-192)
  • AsyncSearchStatus_AsyncSearchStatusCanceled (191-191)
pkg/seqproxyapi/v1/mappings.go (2)
  • ToProtoAsyncSearchStatus (101-106)
  • MustProtoAsyncSearchStatus (108-114)
proxyapi/mock/grpc_v1.go (2)
proxy/search/async.go (4)
  • FetchAsyncSearchResultRequest (80-85)
  • FetchAsyncSearchResultResponse (87-101)
  • GetAsyncSearchesListRequest (103-108)
  • AsyncSearchesListItem (110-122)
pkg/seqproxyapi/v1/seq_proxy_api.pb.go (12)
  • FetchAsyncSearchResultRequest (1171-1184)
  • FetchAsyncSearchResultRequest (1197-1197)
  • FetchAsyncSearchResultRequest (1212-1214)
  • FetchAsyncSearchResultResponse (1244-1258)
  • FetchAsyncSearchResultResponse (1271-1271)
  • FetchAsyncSearchResultResponse (1286-1288)
  • GetAsyncSearchesListRequest (1506-1517)
  • GetAsyncSearchesListRequest (1530-1530)
  • GetAsyncSearchesListRequest (1545-1547)
  • AsyncSearchesListItem (1621-1635)
  • AsyncSearchesListItem (1648-1648)
  • AsyncSearchesListItem (1663-1665)
pkg/seqproxyapi/v1/mappings.go (4)
fracmanager/async_searcher.go (5)
  • AsyncSearchStatus (627-627)
  • AsyncSearchStatusDone (630-630)
  • AsyncSearchStatusInProgress (631-631)
  • AsyncSearchStatusError (632-632)
  • AsyncSearchStatusCanceled (633-633)
pkg/storeapi/store_api.pb.go (8)
  • AsyncSearchStatus (186-186)
  • AsyncSearchStatus (221-223)
  • AsyncSearchStatus (225-227)
  • AsyncSearchStatus (234-236)
  • AsyncSearchStatus_AsyncSearchStatusDone (190-190)
  • AsyncSearchStatus_AsyncSearchStatusInProgress (189-189)
  • AsyncSearchStatus_AsyncSearchStatusError (192-192)
  • AsyncSearchStatus_AsyncSearchStatusCanceled (191-191)
pkg/seqproxyapi/v1/seq_proxy_api.pb.go (8)
  • AsyncSearchStatus (189-189)
  • AsyncSearchStatus (230-232)
  • AsyncSearchStatus (234-236)
  • AsyncSearchStatus (243-245)
  • AsyncSearchStatus_AsyncSearchStatusDone (196-196)
  • AsyncSearchStatus_AsyncSearchStatusInProgress (194-194)
  • AsyncSearchStatus_AsyncSearchStatusError (201-201)
  • AsyncSearchStatus_AsyncSearchStatusCanceled (198-198)
pkg/storeapi/mappings.go (2)
  • ToProtoAsyncSearchStatus (130-135)
  • MustProtoAsyncSearchStatus (137-143)
storeapi/grpc_async_search.go (7)
storeapi/grpc_v1.go (1)
  • GrpcV1 (83-98)
pkg/storeapi/store_api.pb.go (26)
  • StartAsyncSearchRequest (716-728)
  • StartAsyncSearchRequest (741-741)
  • StartAsyncSearchRequest (756-758)
  • Order (88-88)
  • Order (117-119)
  • Order (121-123)
  • Order (130-132)
  • FetchAsyncSearchResultRequest (852-860)
  • FetchAsyncSearchResultRequest (873-873)
  • FetchAsyncSearchResultRequest (888-890)
  • FetchAsyncSearchResultResponse (920-939)
  • FetchAsyncSearchResultResponse (952-952)
  • FetchAsyncSearchResultResponse (967-969)
  • GetAsyncSearchesListRequest (1236-1242)
  • GetAsyncSearchesListRequest (1255-1255)
  • GetAsyncSearchesListRequest (1270-1272)
  • GetAsyncSearchesListResponse (1288-1293)
  • GetAsyncSearchesListResponse (1306-1306)
  • GetAsyncSearchesListResponse (1321-1323)
  • AsyncSearchStatus (186-186)
  • AsyncSearchStatus (221-223)
  • AsyncSearchStatus (225-227)
  • AsyncSearchStatus (234-236)
  • AsyncSearchesListItem (1332-1351)
  • AsyncSearchesListItem (1364-1364)
  • AsyncSearchesListItem (1379-1381)
frac/processor/search_params.go (1)
  • SearchParams (16-28)
seq/seq.go (2)
  • MID (17-17)
  • ID (12-15)
seq/qpr.go (2)
  • DocsOrderDesc (19-19)
  • QPR (68-74)
fracmanager/async_searcher.go (5)
  • AsyncSearchRequest (132-137)
  • FetchSearchResultRequest (621-625)
  • GetAsyncSearchesListRequest (927-930)
  • AsyncSearchStatus (627-627)
  • AsyncSearchesListItem (932-952)
pkg/storeapi/mappings.go (1)
  • MustProtoAsyncSearchStatus (137-143)
fracmanager/async_searcher.go (7)
storeapi/grpc_v1.go (1)
  • MappingProvider (24-26)
fracmanager/list.go (1)
  • List (10-10)
parser/seqql.go (1)
  • ParseSeqQL (28-58)
frac/fraction.go (2)
  • Fraction (18-24)
  • DataProvider (13-16)
util/bufferpool.go (1)
  • BufferPool (9-11)
seq/qpr.go (4)
  • QPR (68-74)
  • DocsOrder (16-16)
  • ErrorSource (61-64)
  • MergeQPRs (355-400)
bytespool/bytespool.go (2)
  • Acquire (22-25)
  • Release (28-30)
proxy/search/async.go (5)
pkg/storeapi/store_api.pb.go (32)
  • AggQuery (342-351)
  • AggQuery (364-364)
  • AggQuery (379-381)
  • Order (88-88)
  • Order (117-119)
  • Order (121-123)
  • Order (130-132)
  • AsyncSearchStatus (186-186)
  • AsyncSearchStatus (221-223)
  • AsyncSearchStatus (225-227)
  • AsyncSearchStatus (234-236)
  • FetchAsyncSearchResultRequest (852-860)
  • FetchAsyncSearchResultRequest (873-873)
  • FetchAsyncSearchResultRequest (888-890)
  • FetchAsyncSearchResultResponse (920-939)
  • FetchAsyncSearchResultResponse (952-952)
  • FetchAsyncSearchResultResponse (967-969)
  • GetAsyncSearchesListRequest (1236-1242)
  • GetAsyncSearchesListRequest (1255-1255)
  • GetAsyncSearchesListRequest (1270-1272)
  • AsyncSearchesListItem (1332-1351)
  • AsyncSearchesListItem (1364-1364)
  • AsyncSearchesListItem (1379-1381)
  • GetAsyncSearchesListResponse (1288-1293)
  • GetAsyncSearchesListResponse (1306-1306)
  • GetAsyncSearchesListResponse (1321-1323)
  • CancelAsyncSearchRequest (1076-1081)
  • CancelAsyncSearchRequest (1094-1094)
  • CancelAsyncSearchRequest (1109-1111)
  • DeleteAsyncSearchRequest (1156-1161)
  • DeleteAsyncSearchRequest (1174-1174)
  • DeleteAsyncSearchRequest (1189-1191)
seq/qpr.go (3)
  • DocsOrder (16-16)
  • QPR (68-74)
  • MergeQPRs (355-400)
fracmanager/async_searcher.go (7)
  • AsyncSearchStatus (627-627)
  • AsyncSearchStatusDone (630-630)
  • GetAsyncSearchesListRequest (927-930)
  • AsyncSearchesListItem (932-952)
  • AsyncSearchStatusInProgress (631-631)
  • AsyncSearchStatusCanceled (633-633)
  • AsyncSearchStatusError (632-632)
proxy/search/ingestor.go (1)
  • Ingestor (36-41)
pkg/storeapi/store_api_vtproto.pb.go (1)
  • StoreApiClient (1659-1669)
tests/integration_tests/integration_test.go (3)
proxy/search/async.go (3)
  • AsyncRequest (24-32)
  • FetchAsyncSearchResultRequest (80-85)
  • GetAsyncSearchesListRequest (103-108)
seq/qpr.go (2)
  • QPR (68-74)
  • ErrorSource (61-64)
fracmanager/async_searcher.go (2)
  • AsyncSearchStatusDone (630-630)
  • GetAsyncSearchesListRequest (927-930)
proxyapi/grpc_async_search.go (5)
pkg/seqproxyapi/v1/seq_proxy_api.pb.go (35)
  • StartAsyncSearchRequest (1043-1059)
  • StartAsyncSearchRequest (1072-1072)
  • StartAsyncSearchRequest (1087-1089)
  • FetchAsyncSearchResultRequest (1171-1184)
  • FetchAsyncSearchResultRequest (1197-1197)
  • FetchAsyncSearchResultRequest (1212-1214)
  • FetchAsyncSearchResultResponse (1244-1258)
  • FetchAsyncSearchResultResponse (1271-1271)
  • FetchAsyncSearchResultResponse (1286-1288)
  • Order (143-143)
  • Order (172-174)
  • Order (176-178)
  • Order (185-187)
  • SearchQuery (460-468)
  • SearchQuery (481-481)
  • SearchQuery (496-498)
  • HistQuery (606-611)
  • HistQuery (624-624)
  • HistQuery (639-641)
  • ComplexSearchResponse (949-961)
  • ComplexSearchResponse (974-974)
  • ComplexSearchResponse (989-991)
  • GetAsyncSearchesListRequest (1506-1517)
  • GetAsyncSearchesListRequest (1530-1530)
  • GetAsyncSearchesListRequest (1545-1547)
  • AsyncSearchStatus (189-189)
  • AsyncSearchStatus (230-232)
  • AsyncSearchStatus (234-236)
  • AsyncSearchStatus (243-245)
  • AggQuery (529-538)
  • AggQuery (551-551)
  • AggQuery (566-568)
  • AsyncSearchesListItem (1621-1635)
  • AsyncSearchesListItem (1648-1648)
  • AsyncSearchesListItem (1663-1665)
seq/seq.go (3)
  • MID (17-17)
  • ID (12-15)
  • MIDToDuration (111-113)
proxy/search/async.go (4)
  • FetchAsyncSearchResultRequest (80-85)
  • FetchAsyncSearchResultResponse (87-101)
  • GetAsyncSearchesListRequest (103-108)
  • AsyncSearchesListItem (110-122)
pkg/seqproxyapi/v1/mappings.go (1)
  • MustProtoAsyncSearchStatus (108-114)
fracmanager/async_searcher.go (3)
  • GetAsyncSearchesListRequest (927-930)
  • AsyncSearchStatus (627-627)
  • AsyncSearchesListItem (932-952)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (27)
frac/processor/search_params.go (1)

16-18: Hiding AST from JSON is the right call

Omitting the parsed AST from JSON payloads prevents accidental large/cyclic encodes and keeps the API surface clean. Looks good.

cmd/seq-db/seq-db.go (1)

290-294: Guard against int overflow and confirm DataDir defaulting path

  • Casting Bytes to int can overflow on 32-bit builds or with large limits. Either clamp before casting or prefer int64/uint64 in AsyncSearcherConfig.
  • The comment in config hints DataDir defaults to a subdir of Storage.DataDir; here we pass it through as-is. Please confirm fracmanager applies that default when empty to avoid writing in CWD.

If keeping int, consider clamping locally:

// helper
func clampToInt[T ~int64 | ~uint64](v T) int {
    if v > T(math.MaxInt) {
        return math.MaxInt
    }
    return int(v)
}

// use
MaxSize:           clampToInt(cfg.AsyncSearch.MaxTotalSize),
MaxSizePerRequest: clampToInt(cfg.AsyncSearch.MaxSizePerRequest),
seq/qpr.go (1)

372-377: Init guard change avoids needless allocs; semantics preserved

Switching to len checks prevents allocating zero-length slices and keeps behavior intact. Good micro-fix.

proxyapi/mock/grpc_v1.go (2)

44-70: New Cancel/Delete async-search mocks look correct

Signatures align with the expanded interface. No issues from me.


103-116: List API mock addition matches the new surface

GetAsyncSearchesList mock and recorder look aligned with the request/response types.

api/storeapi/store_api.proto (2)

20-25: New async-search lifecycle RPCs look good

Cancel/Delete/List expand the lifecycle surface cleanly and match the broader PR scope.


201-208: Filters for GetAsyncSearchesList look reasonable

optional status + ids filter is a pragmatic start. Presence on enum is fine in proto3.

proxyapi/ingestor.go (1)

223-225: Unmarshal added — completes the Marshaler contract

This makes humanReadableMarshaler bidirectional with JSONBuiltin. Good.

proxy/search/ingestor.go (1)

137-138: Good extraction: paginateIDs moved to package-level

Reduces receiver coupling; call site updated correctly.

pkg/storeapi/mappings.go (1)

100-113: LGTM: bidirectional status mappings are consistent with enum values

The mapping aligns fracmanager statuses [Done, InProgress, Error, Canceled] to proto enum values [Done=1, InProgress=0, Error=3, Canceled=2], and the reverse builder is correct.

storeapi/client.go (1)

41-51: LGTM: thin client forwarders mirror new RPCs

Straight-through delegation matches existing pattern; ignoring grpc.CallOption is consistent with other methods in this file.

pkg/seqproxyapi/v1/marshaler.go (2)

223-225: StartAsyncSearchRequest JSON marshalling: LGTM

Delegating to the shared protojson marshaller with EmitDefaultValues=true is consistent with the rest of the package.


258-260: AsyncSearchesListItem JSON marshalling: LGTM

Using the shared protojson marshaller keeps consistency for list items.

pkg/seqproxyapi/v1/mappings.go (1)

71-76: Status mappings between fracmanager and proto: LGTM

Index-based mapping aligns with the declared enum orders in both systems.

Also applies to: 78-85

proxyapi/grpc_v1.go (1)

33-37: Interface surface update: LGTM; verify implementers/mocks are updated.

New async-search methods and the FetchAsyncSearchResult signature change are coherent with the broader API changes. Ensure all SearchIngestor implementations and mocks are updated to satisfy the new interface.

tests/integration_tests/integration_test.go (2)

1947-1976: Async-search happy-path coverage: LGTM

End-to-end start→fetch flow validations (status, exp/agg/hist/progress) are solid and representative.


2042-2052: List/delete flow assertions: LGTM

Validates ordering, status, request echo, expiry and progress; delete is asserted via list shrinking. Good coverage.

storeapi/grpc_async_search.go (2)

96-102: CancelAsyncSearch/DeleteAsyncSearch: LGTM

Idempotent behavior is fine for now. If needed later, consider surfacing NotFound for unknown IDs.


150-179: convertAsyncSearchesToProto: LGTM

Clean translation of fields, including conditional canceled_at and duration/timestamps.

proxy/search/mock/store_api_client_mock.go (3)

60-78: LGTM!

The mock implementation for CancelAsyncSearch follows the established pattern correctly.


80-98: LGTM!

The mock implementation for DeleteAsyncSearch is consistent with other mock methods.


140-158: LGTM!

The mock implementation for GetAsyncSearchesList follows the established pattern correctly.

fracmanager/encoding.go (1)

424-426: Fix the status weight mapping.

The statusWeight slice is being used incorrectly. You're trying to use the status values as indices, but the status values are 0-3, not 1-4. This will cause a panic or incorrect behavior.

-	statusWeight := []fracmanager.AsyncSearchStatus{
-		fracmanager.AsyncSearchStatusDone:       1,
-		fracmanager.AsyncSearchStatusInProgress: 2,
-		fracmanager.AsyncSearchStatusCanceled:   3,
-		fracmanager.AsyncSearchStatusError:      4,
-	}
-	weightA := statusWeight[a]
-	weightB := statusWeight[b]
+	statusWeight := map[fracmanager.AsyncSearchStatus]int{
+		fracmanager.AsyncSearchStatusDone:       1,
+		fracmanager.AsyncSearchStatusInProgress: 2,
+		fracmanager.AsyncSearchStatusCanceled:   3,
+		fracmanager.AsyncSearchStatusError:      4,
+	}
+	weightA := statusWeight[a]
+	weightB := statusWeight[b]

Likely an incorrect or invalid review comment.

proxyapi/grpc_async_search.go (2)

27-33: Validate histogram interval parsing.

Good error handling for histogram interval parsing.


56-61: LGTM!

The streaming approach for fetching async search results with docs iterator is well-designed.

proxy/search/async.go (1)

267-275: LGTM!

The pagination logic for IDs is correctly implemented with proper offset and size handling.

fracmanager/async_searcher.go (1)

169-188: Avoid potential data race on atomic fields

The merged, qprsSize, and infoSize atomic fields are being stored directly in the map, but the asyncSearchInfo struct is passed by value in several places. This could lead to copying the atomic values, which is not safe.

Consider either:

  1. Store pointers to asyncSearchInfo in the map instead of values
  2. Make the atomic fields regular fields protected by the mutex

Here's the recommended approach to store pointers:

-requests   map[string]asyncSearchInfo
+requests   map[string]*asyncSearchInfo

And update the constructor:

-func newAsyncSearchInfo(r AsyncSearchRequest, list List) asyncSearchInfo {
+func newAsyncSearchInfo(r AsyncSearchRequest, list List) *asyncSearchInfo {
 	fracsToSearch := make([]fracSearchState, 0, len(list))
 	for _, f := range list {
 		fracsToSearch = append(fracsToSearch, fracSearchState{Name: f.Info().Name()})
 	}
 	ctx, cancel := context.WithCancel(context.Background())
-	return asyncSearchInfo{
+	return &asyncSearchInfo{
 		Finished:   false,
 		Error:      "",
 		CanceledAt: time.Time{},
 		ctx:        ctx,
 		cancel:     cancel,
 		Request:    r,
 		Fractions:  fracsToSearch,
 		StartedAt:  time.Now(),
 		merged:     &atomic.Bool{},
 		qprsSize:   &atomic.Int64{},
 		infoSize:   &atomic.Int64{},
 	}
}

Likely an incorrect or invalid review comment.

Comment thread api/storeapi/store_api.proto
Comment thread fracmanager/async_searcher_test.go
Comment thread fracmanager/async_searcher.go
Comment thread fracmanager/async_searcher.go
Comment thread fracmanager/async_searcher.go
Comment thread fracmanager/async_searcher.go
Comment thread fracmanager/encoding.go
Comment thread pkg/seqproxyapi/v1/marshaler.go
Comment thread pkg/storeapi/mappings.go
Comment thread proxyapi/mock/grpc_v1.go
@forshev forshev force-pushed the 214-async-search-proto branch from f87bc21 to 3023ae8 Compare August 20, 2025 14:22
Copy link
Copy Markdown

@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: 0

♻️ Duplicate comments (10)
api/seqproxyapi/v1/seq_proxy_api.proto (2)

278-289: Well-structured async search status enum

The status enum provides clear state transitions with helpful documentation. Consider adding a pending state per past review suggestion if scheduling becomes relevant.


267-276: Document pagination parameter behavior when with_docs=false

The comments indicate these fields are ignored when with_docs is false, but server-side validation would be clearer than silent ignoring.

api/storeapi/store_api.proto (3)

142-151: Client-supplied search_id creates security risks

The client provides the search_id which shifts uniqueness responsibility to the client and could enable ID collision attacks.


174-174: Remove redundant 'optional' on message-typed field

Proto3 message fields already track presence; the optional modifier is unnecessary and may cause compilation issues.

-	optional google.protobuf.Timestamp canceled_at = 5;
+	google.protobuf.Timestamp canceled_at = 5;

215-215: Remove redundant 'optional' on canceled_at field

Same issue as above - optional is unnecessary on message-typed fields.

-	optional google.protobuf.Timestamp canceled_at = 5;
+	google.protobuf.Timestamp canceled_at = 5;
fracmanager/async_searcher.go (5)

270-279: Potential goroutine leak from struct copying

The updateSearchInfo function updates a copy of asyncSearchInfo which contains ctx and cancel fields. This breaks context cancellation propagation.


288-298: Atomic fields won't serialize correctly to JSON

The asyncSearchInfo struct contains atomic pointer fields that won't marshal/unmarshal properly, causing issues when loading from disk.


419-431: Non-atomic disk usage calculation

Reading two atomic values separately can yield inconsistent results. Load atomics once into locals before computing.


578-589: Missing context initialization after unmarshaling

Loaded asyncSearchInfo has nil ctx and cancel after unmarshaling, causing nil pointer dereferences.


843-849: Race condition during lock release and reacquisition

Releasing read lock to call updateSearchInfo allows map mutations between operations.

🧹 Nitpick comments (1)
tests/integration_tests/integration_test.go (1)

1979-1989: Consider adding timeout to Eventually assertion

The polling loop could hang indefinitely if the async search never completes. Consider adding a more explicit error message when timeout occurs.

-	r.Eventually(func() bool {
-		resp, _, err := searcher.FetchAsyncSearchResult(ctx, freq)
-		r.NoError(err)
-		return resp.Status == fracmanager.AsyncSearchStatusDone
-	}, 10*time.Second, 50*time.Millisecond)
+	r.Eventually(func() bool {
+		resp, _, err := searcher.FetchAsyncSearchResult(ctx, freq)
+		r.NoError(err)
+		return resp.Status == fracmanager.AsyncSearchStatusDone
+	}, 10*time.Second, 50*time.Millisecond, "async search did not complete within timeout")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f87bc21 and 3023ae8.

⛔ Files ignored due to path filters (6)
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.go is excluded by !**/*.pb.go
  • pkg/seqproxyapi/v1/seq_proxy_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/seqproxyapi/v1/seq_proxy_api_vtproto.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.go is excluded by !**/*.pb.go
  • pkg/storeapi/store_api.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/storeapi/store_api_vtproto.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (27)
  • api/seqproxyapi/v1/seq_proxy_api.proto (4 hunks)
  • api/storeapi/store_api.proto (2 hunks)
  • cmd/seq-db/seq-db.go (1 hunks)
  • config/config.go (1 hunks)
  • frac/meta_data_collector.go (1 hunks)
  • frac/processor/search_params.go (1 hunks)
  • fracmanager/async_searcher.go (6 hunks)
  • fracmanager/async_searcher_test.go (1 hunks)
  • fracmanager/encoding.go (1 hunks)
  • fracmanager/encoding_test.go (1 hunks)
  • pkg/seqproxyapi/v1/mappings.go (2 hunks)
  • pkg/seqproxyapi/v1/marshaler.go (1 hunks)
  • pkg/seqproxyapi/v1/marshaler_test.go (1 hunks)
  • pkg/storeapi/mappings.go (2 hunks)
  • proxy/search/async.go (4 hunks)
  • proxy/search/ingestor.go (2 hunks)
  • proxy/search/mock/store_api_client_mock.go (2 hunks)
  • proxyapi/grpc_async_search.go (3 hunks)
  • proxyapi/grpc_v1.go (1 hunks)
  • proxyapi/ingestor.go (1 hunks)
  • proxyapi/mock/grpc_v1.go (3 hunks)
  • seq/qpr.go (1 hunks)
  • storeapi/client.go (1 hunks)
  • storeapi/grpc_async_search.go (3 hunks)
  • storeapi/grpc_v1.go (1 hunks)
  • tests/integration_tests/integration_test.go (4 hunks)
  • util/bufferpool.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (22)
  • util/bufferpool.go
  • proxyapi/ingestor.go
  • proxy/search/mock/store_api_client_mock.go
  • frac/meta_data_collector.go
  • cmd/seq-db/seq-db.go
  • frac/processor/search_params.go
  • config/config.go
  • storeapi/grpc_v1.go
  • storeapi/grpc_async_search.go
  • fracmanager/encoding_test.go
  • pkg/seqproxyapi/v1/marshaler.go
  • fracmanager/encoding.go
  • seq/qpr.go
  • proxyapi/mock/grpc_v1.go
  • storeapi/client.go
  • proxyapi/grpc_v1.go
  • pkg/seqproxyapi/v1/mappings.go
  • pkg/seqproxyapi/v1/marshaler_test.go
  • proxyapi/grpc_async_search.go
  • fracmanager/async_searcher_test.go
  • proxy/search/ingestor.go
  • pkg/storeapi/mappings.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: forshev
PR: ozontech/seq-db#3
File: api/seqproxyapi/v1/seq_proxy_api.proto:88-90
Timestamp: 2025-08-05T10:52:04.737Z
Learning: In the seq-db project, the FetchAsyncSearchResult RPC method in api/seqproxyapi/v1/seq_proxy_api.proto was intentionally changed from HTTP GET to POST during development. This breaking change is acceptable because the async search feature is under active development and not yet in production use.
📚 Learning: 2025-08-05T10:52:04.737Z
Learnt from: forshev
PR: ozontech/seq-db#3
File: api/seqproxyapi/v1/seq_proxy_api.proto:88-90
Timestamp: 2025-08-05T10:52:04.737Z
Learning: In the seq-db project, the FetchAsyncSearchResult RPC method in api/seqproxyapi/v1/seq_proxy_api.proto was intentionally changed from HTTP GET to POST during development. This breaking change is acceptable because the async search feature is under active development and not yet in production use.

Applied to files:

  • api/seqproxyapi/v1/seq_proxy_api.proto
  • proxy/search/async.go
  • api/storeapi/store_api.proto
📚 Learning: 2025-08-05T10:51:10.548Z
Learnt from: forshev
PR: ozontech/seq-db#3
File: proxy/search/async.go:155-171
Timestamp: 2025-08-05T10:51:10.548Z
Learning: In proxy/search/async.go, the FetchAsyncSearchResult method uses an `anyResponse` variable to track if any successful responses were received from any shard. If all shards return NotFound errors, the function properly returns a NotFound status error, so individual shard-level response tracking is not necessary.

Applied to files:

  • api/seqproxyapi/v1/seq_proxy_api.proto
  • tests/integration_tests/integration_test.go
  • proxy/search/async.go
  • api/storeapi/store_api.proto
🧬 Code Graph Analysis (3)
fracmanager/async_searcher.go (7)
storeapi/grpc_v1.go (1)
  • MappingProvider (24-26)
fracmanager/list.go (1)
  • List (10-10)
parser/seqql.go (1)
  • ParseSeqQL (28-58)
frac/fraction.go (2)
  • Fraction (18-24)
  • DataProvider (13-16)
util/bufferpool.go (1)
  • BufferPool (9-11)
seq/qpr.go (4)
  • QPR (68-74)
  • DocsOrder (16-16)
  • ErrorSource (61-64)
  • MergeQPRs (355-400)
bytespool/bytespool.go (2)
  • Acquire (22-25)
  • Release (28-30)
tests/integration_tests/integration_test.go (3)
proxy/search/async.go (3)
  • AsyncRequest (24-32)
  • FetchAsyncSearchResultRequest (80-85)
  • GetAsyncSearchesListRequest (103-108)
seq/qpr.go (2)
  • QPR (68-74)
  • ErrorSource (61-64)
fracmanager/async_searcher.go (2)
  • AsyncSearchStatusDone (630-630)
  • GetAsyncSearchesListRequest (927-930)
proxy/search/async.go (6)
pkg/storeapi/store_api.pb.go (32)
  • AggQuery (342-351)
  • AggQuery (364-364)
  • AggQuery (379-381)
  • Order (88-88)
  • Order (117-119)
  • Order (121-123)
  • Order (130-132)
  • AsyncSearchStatus (186-186)
  • AsyncSearchStatus (221-223)
  • AsyncSearchStatus (225-227)
  • AsyncSearchStatus (234-236)
  • FetchAsyncSearchResultRequest (852-860)
  • FetchAsyncSearchResultRequest (873-873)
  • FetchAsyncSearchResultRequest (888-890)
  • FetchAsyncSearchResultResponse (920-939)
  • FetchAsyncSearchResultResponse (952-952)
  • FetchAsyncSearchResultResponse (967-969)
  • GetAsyncSearchesListRequest (1236-1242)
  • GetAsyncSearchesListRequest (1255-1255)
  • GetAsyncSearchesListRequest (1270-1272)
  • AsyncSearchesListItem (1332-1351)
  • AsyncSearchesListItem (1364-1364)
  • AsyncSearchesListItem (1379-1381)
  • GetAsyncSearchesListResponse (1288-1293)
  • GetAsyncSearchesListResponse (1306-1306)
  • GetAsyncSearchesListResponse (1321-1323)
  • CancelAsyncSearchRequest (1076-1081)
  • CancelAsyncSearchRequest (1094-1094)
  • CancelAsyncSearchRequest (1109-1111)
  • DeleteAsyncSearchRequest (1156-1161)
  • DeleteAsyncSearchRequest (1174-1174)
  • DeleteAsyncSearchRequest (1189-1191)
proxy/search/search_request.go (1)
  • AggQuery (9-15)
seq/qpr.go (4)
  • DocsOrder (16-16)
  • QPR (68-74)
  • ErrorSource (61-64)
  • MergeQPRs (355-400)
fracmanager/async_searcher.go (7)
  • AsyncSearchStatus (627-627)
  • AsyncSearchStatusDone (630-630)
  • GetAsyncSearchesListRequest (927-930)
  • AsyncSearchesListItem (932-952)
  • AsyncSearchStatusInProgress (631-631)
  • AsyncSearchStatusCanceled (633-633)
  • AsyncSearchStatusError (632-632)
proxy/search/ingestor.go (1)
  • Ingestor (36-41)
pkg/storeapi/store_api_vtproto.pb.go (1)
  • StoreApiClient (1659-1669)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (5)
tests/integration_tests/integration_test.go (2)

1951-1971: Async search request structure looks good

The construction of startReq with retention, aggregations, and histogram interval is well-structured and properly uses UTC timestamps.


2042-2063: Comprehensive list validation

Good test coverage for GetAsyncSearchesList including sorting order verification and cross-field consistency checks.

proxy/search/async.go (3)

41-44: Missing error handling for getAsyncSearchStores

Good addition of error checking here. The error properly propagates up the call stack.


423-435: Inconsistent status weight indexing

The statusWeight slice uses status values as array indices, but the values are 1-4 while the status constants are 0-3. This causes an out-of-bounds access.

Fix the weight map to use proper indices:

 func mergeAsyncSearchStatus(a, b fracmanager.AsyncSearchStatus) fracmanager.AsyncSearchStatus {
-	statusWeight := []fracmanager.AsyncSearchStatus{
-		fracmanager.AsyncSearchStatusDone:       1,
-		fracmanager.AsyncSearchStatusInProgress: 2,
-		fracmanager.AsyncSearchStatusCanceled:   3,
-		fracmanager.AsyncSearchStatusError:      4,
-	}
-	weightA := statusWeight[a]
-	weightB := statusWeight[b]
+	statusWeight := map[fracmanager.AsyncSearchStatus]int{
+		fracmanager.AsyncSearchStatusDone:       1,
+		fracmanager.AsyncSearchStatusInProgress: 2,
+		fracmanager.AsyncSearchStatusCanceled:   3,
+		fracmanager.AsyncSearchStatusError:      4,
+	}
+	weightA := statusWeight[a]
+	weightB := statusWeight[b]
 	if weightA >= weightB {
 		return a
 	}
 	return b
 }

Likely an incorrect or invalid review comment.


148-177: Potential goroutine leak in concurrent shard processing

The goroutines started here don't check the context cancellation inside the loop. If the context is cancelled while iterating replicas, the goroutines will continue running unnecessarily.

Add context checking inside the replica loop:

 		go func(shard []string) {
 			defer wg.Done()
 
 			for _, replica := range shard {
+				select {
+				case <-storesCtx.Done():
+					return
+				default:
+				}
 				storeResp, err := si.clients[replica].FetchAsyncSearchResult(storesCtx, &req)
⛔ Skipped due to learnings
Learnt from: forshev
PR: ozontech/seq-db#3
File: proxy/search/async.go:155-171
Timestamp: 2025-08-05T10:51:10.548Z
Learning: In proxy/search/async.go, the FetchAsyncSearchResult method uses an `anyResponse` variable to track if any successful responses were received from any shard. If all shards return NotFound errors, the function properly returns a NotFound status error, so individual shard-level response tracking is not necessary.

@forshev forshev merged commit ae26c0d into main Aug 20, 2025
5 checks passed
@forshev forshev deleted the 214-async-search-proto branch August 20, 2025 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants