UNPKG

convex

Version:

Client for the Convex Cloud

149 lines (146 loc) 5.28 kB
"use strict"; import { chalkStderr } from "chalk"; import { changeSpinner, logFinishedStep, logMessage, stopSpinner } from "../../bundler/log.js"; import { formatIndex } from "./indexes.js"; import { promptYesNo } from "./utils/prompts.js"; import { evaluatePush } from "./deploy2.js"; import { runSystemQuery } from "./run.js"; const MIN_DOCUMENTS_FOR_INDEX_DELETE_WARNING = 1e5; export async function checkForLargeIndexDeletion({ ctx, span, request, options, askForConfirmation }) { changeSpinner("Verifying that the push isn\u2019t deleting large indexes..."); const { schemaChange } = await evaluatePush(ctx, span, request, options); const indexDiffs = schemaChange.indexDiffs ?? {}; const deletedIndexes = Object.entries(indexDiffs).flatMap( ([componentDefinitionPath, indexDiff]) => indexDiff.removed_indexes.map((index) => ({ componentDefinitionPath, index })) ); if (deletedIndexes.length === 0) { logFinishedStep("No indexes are deleted by this push"); return; } const tablesWithDeletedIndexes = [ ...new Set( deletedIndexes.map( ({ componentDefinitionPath, index }) => `${componentDefinitionPath}:${getTableName(index)}` ) ) ].map((str) => { const [componentDefinitionPath, table] = str.split(":"); return { componentDefinitionPath, table }; }); changeSpinner("Checking whether the deleted indexes are on large tables..."); const documentCounts = await Promise.all( tablesWithDeletedIndexes.map( async ({ componentDefinitionPath, table }) => ({ componentDefinitionPath, table, count: await runSystemQuery(ctx, { deploymentUrl: options.url, adminKey: options.adminKey, functionName: "_system/cli/tableSize:default", componentPath: componentDefinitionPath, args: { tableName: table } }) }) ) ); const deletedIndexesWithDocumentsCount = deletedIndexes.map( ({ componentDefinitionPath, index }) => ({ componentDefinitionPath, index, count: documentCounts.find( (count) => count.table === getTableName(index) && count.componentDefinitionPath === componentDefinitionPath ).count }) ); const minDocumentsForWarning = minDocumentsForIndexDeleteWarning(); if (!deletedIndexesWithDocumentsCount.some( ({ count }) => count >= minDocumentsForWarning )) { logFinishedStep("No large indexes are deleted by this push"); return; } logMessage(`\u26A0\uFE0F This code push will ${chalkStderr.bold("delete")} the following ${deletedIndexesWithDocumentsCount.length === 1 ? "index" : "indexes"} from your production deployment (${options.url}): ${deletedIndexesWithDocumentsCount.map( ({ componentDefinitionPath, index, count }) => formatDeletedIndex({ componentDefinitionPath, index, indexDiff: indexDiffs[componentDefinitionPath], documentsCount: count, minDocumentsForWarning }) ).join("\n")} The documents that are in the index won\u2019t be deleted, but the index will need to be backfilled again if you want to restore it later. `); if (!askForConfirmation) { logFinishedStep( "Proceeding with push since --allow-deleting-large-indexes is set" ); return; } if (!process.stdin.isTTY) { return ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `To confirm the push: \u2022 run the deploy command in an ${chalkStderr.bold("interactive terminal")} \u2022 or run the deploy command with the ${chalkStderr.bold("--allow-deleting-large-indexes")} flag` }); } stopSpinner(); if (!await promptYesNo(ctx, { message: `Delete ${deletedIndexesWithDocumentsCount.length === 1 ? "this index" : "these indexes"}?`, default: false })) { return ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `Canceling push` }); } logFinishedStep("Proceeding with push."); } function formatDeletedIndex({ componentDefinitionPath, index, indexDiff, documentsCount, minDocumentsForWarning }) { const componentNameFormatted = componentDefinitionPath !== "" ? `${chalkStderr.gray(componentDefinitionPath)}:` : ""; const documentsCountFormatted = documentsCount >= minDocumentsForWarning ? ` ${chalkStderr.yellowBright(`\u26A0\uFE0F ${documentsCount.toLocaleString()} documents`)}` : ` ${documentsCount.toLocaleString()} ${documentsCount === 1 ? "document" : "documents"}`; const replacedBy = indexDiff.added_indexes.find((i) => i.name === index.name); const replacedByFormatted = replacedBy ? ` ${chalkStderr.green("\u2192 replaced by:")} ${formatIndex(replacedBy)}` : ""; return "\u26D4 " + componentNameFormatted + formatIndex(index) + documentsCountFormatted + replacedByFormatted; } function getTableName(index) { const [tableName, _indexName] = index.name.split("."); return tableName; } function minDocumentsForIndexDeleteWarning() { const envValue = process.env.CONVEX_MIN_DOCUMENTS_FOR_INDEX_DELETE_WARNING; if (envValue !== void 0) { const parsed = parseInt(envValue, 10); if (!isNaN(parsed)) { return parsed; } } return MIN_DOCUMENTS_FOR_INDEX_DELETE_WARNING; } //# sourceMappingURL=checkForLargeIndexDeletion.js.map