fix(elasticsearch): clamp page size to max_result_window
This commit is contained in:
1 parent
aac09d3cb7
commit
ff88516235
3 files changed
+39
-7
No files matched your search
@@ -22,7 +22,7 @@ import type { DynamoDbIndexInfo, DynamoDbTableDescription } from "@/lib/backend/
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { clampSearchSplitWidth } from "@/lib/dataGrid/dataGridSearchSplit";
|
||||
import { documentViewerFontStyle } from "@/lib/document/documentViewerFontStyle";
|
||||
import { clampDocumentPage, documentPageRequestLimit, resetElasticsearchDocumentTotals, resolveElasticsearchDocumentTotals } from "@/lib/document/elasticsearchDocumentTotals";
|
||||
import { clampDocumentPage, clampElasticsearchRequestLimit, documentPageRequestLimit, resetElasticsearchDocumentTotals, resolveElasticsearchDocumentTotals } from "@/lib/document/elasticsearchDocumentTotals";
|
||||
import { canGoNextDocumentPage, isSameDocumentQueryTotalCountRequest, resolveDocumentQueryTotals, type DocumentQueryTotalCountRequest } from "@/lib/document/documentQueryTotals";
|
||||
import {
|
||||
arrayObjectAncestorPathForDocumentField,
|
||||
@@ -246,7 +246,7 @@ const canGoNextPage = computed(() => {
|
||||
});
|
||||
});
|
||||
const documentRequestLimit = computed(() => {
|
||||
if (documentStoreProvider.value.kind !== "elasticsearch" || paginationTotal.value === undefined) return pageSize.value;
|
||||
if (documentStoreProvider.value.kind !== "elasticsearch") return pageSize.value;
|
||||
return documentPageRequestLimit(page.value, pageSize.value, paginationTotal.value);
|
||||
});
|
||||
|
||||
@@ -1357,7 +1357,10 @@ async function load(options: { page?: number; append?: boolean; offset?: number;
|
||||
throw new Error(t("dynamodb.pageCursorUnavailable"));
|
||||
}
|
||||
const skip = storeKind === "dynamodb" ? 0 : (options.offset ?? requestPage * pageSize.value);
|
||||
const requestLimit = options.limit ?? (storeKind === "elasticsearch" && paginationTotal.value !== undefined ? documentPageRequestLimit(requestPage, pageSize.value, paginationTotal.value) : pageSize.value);
|
||||
const requestedLimit = options.limit ?? (storeKind === "elasticsearch" ? documentPageRequestLimit(requestPage, pageSize.value, paginationTotal.value) : pageSize.value);
|
||||
// Elasticsearch rejects from+size beyond index.max_result_window even when an explicit limit
|
||||
// (e.g. from the per-tab page size control) bypassed the default-path clamp above.
|
||||
const requestLimit = storeKind === "elasticsearch" ? clampElasticsearchRequestLimit(skip, requestedLimit) : requestedLimit;
|
||||
const result = await api.documentFindDocuments(connectionId, database, collection, skip, requestLimit, filter, undefined, sort, undefined, executionId, cursor);
|
||||
if (documentLoadExecutionId.value !== executionId) return;
|
||||
if (connectionId !== props.connectionId || database !== props.database || collection !== props.collection || storeKind !== documentStoreProvider.value.kind) return;
|
||||
|
||||
+15
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { clampDocumentPage, documentPageRequestLimit, resetElasticsearchDocumentTotals, resolveElasticsearchDocumentTotals } from "@/lib/document/elasticsearchDocumentTotals";
|
||||
import { clampDocumentPage, clampElasticsearchRequestLimit, documentPageRequestLimit, resetElasticsearchDocumentTotals, resolveElasticsearchDocumentTotals } from "@/lib/document/elasticsearchDocumentTotals";
|
||||
|
||||
describe("Elasticsearch document totals", () => {
|
||||
it("keeps a lower-bound search total separate from an exact background count", () => {
|
||||
@@ -42,4 +42,18 @@ describe("Elasticsearch document totals", () => {
|
||||
expect(documentPageRequestLimit(30, 333, 10_000)).toBe(10);
|
||||
expect(documentPageRequestLimit(0, 100, undefined)).toBe(100);
|
||||
});
|
||||
|
||||
it("caps the initial page request to Elasticsearch's default max_result_window even without a known total (#7231)", () => {
|
||||
// Opening an index for the first time has no paginationTotal yet; a "rows per page" preference
|
||||
// set above 10,000 must not be forwarded to Elasticsearch as-is, or the from+size search fails outright.
|
||||
expect(documentPageRequestLimit(0, 100_000, undefined)).toBe(10_000);
|
||||
expect(documentPageRequestLimit(0, 5_000, undefined)).toBe(5_000);
|
||||
});
|
||||
|
||||
it("clamps an explicit request limit against the remaining from+size window at a given skip", () => {
|
||||
expect(clampElasticsearchRequestLimit(0, 100_000)).toBe(10_000);
|
||||
expect(clampElasticsearchRequestLimit(9_000, 5_000)).toBe(1_000);
|
||||
expect(clampElasticsearchRequestLimit(20_000, 5_000)).toBe(0);
|
||||
expect(clampElasticsearchRequestLimit(0, 500)).toBe(500);
|
||||
});
|
||||
});
|
||||
@@ -41,9 +41,24 @@ export function clampDocumentPage(page: number, pageSize: number, paginationTota
|
||||
return Math.min(normalizedPage, lastPage);
|
||||
}
|
||||
|
||||
// Elasticsearch rejects any from+size combination above this by default (index.max_result_window).
|
||||
// DBX can't know a target index's actual configured value up front, so it clamps requests to the
|
||||
// out-of-the-box default rather than letting an oversized "rows per page" preference reach the cluster as-is.
|
||||
export const ELASTICSEARCH_DEFAULT_MAX_RESULT_WINDOW = 10_000;
|
||||
|
||||
// Clamps a requested page size against the from+size window still available at a given skip offset,
|
||||
// regardless of where the requested limit came from (a settings default or an explicit override).
|
||||
export function clampElasticsearchRequestLimit(skip: number, limit: number): number {
|
||||
const normalizedSkip = Math.max(0, Math.floor(skip));
|
||||
const normalizedLimit = Math.max(1, Math.floor(limit));
|
||||
return Math.min(normalizedLimit, Math.max(0, ELASTICSEARCH_DEFAULT_MAX_RESULT_WINDOW - normalizedSkip));
|
||||
}
|
||||
|
||||
export function documentPageRequestLimit(page: number, pageSize: number, paginationTotal?: number): number {
|
||||
const normalizedPageSize = Math.max(1, Math.floor(pageSize));
|
||||
if (paginationTotal === undefined) return normalizedPageSize;
|
||||
const remaining = paginationTotal - Math.max(0, Math.floor(page)) * normalizedPageSize;
|
||||
return remaining > 0 ? Math.min(normalizedPageSize, remaining) : normalizedPageSize;
|
||||
const skip = Math.max(0, Math.floor(page)) * normalizedPageSize;
|
||||
const cappedPageSize = clampElasticsearchRequestLimit(skip, normalizedPageSize);
|
||||
if (paginationTotal === undefined) return cappedPageSize;
|
||||
const remaining = paginationTotal - skip;
|
||||
return remaining > 0 ? Math.min(cappedPageSize, remaining) : cappedPageSize;
|
||||
}
|
||||
Reference in new issue
Block a user