From 25cc654fd3b0fa66a5445dfaf796a1a43c578c5f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 3 Mar 2025 12:32:46 +0000 Subject: [PATCH] Add locking to syncing SQS definitions during search calls --- .../server/src/api/controllers/application.ts | 3 +- packages/server/src/constants/screens.ts | 297 +++++++++++++++++- .../src/sdk/app/rows/search/internal/sqs.ts | 12 +- packages/types/src/sdk/locks.ts | 1 + 4 files changed, 298 insertions(+), 15 deletions(-) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index c401362bc5..127e86f1a9 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -342,7 +342,8 @@ async function performAppCreate( navigation: "Top", title: name, navWidth: "Large", - navBackground: "var(--spectrum-global-color-gray-50)", + navBackground: "var(--spectrum-global-color-static-blue-800)", + navTextColor: "var(--spectrum-global-color-static-white)", links: newLinks, }, theme: DefaultAppTheme, diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index 06b6ad2f45..44ba3062f8 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -237,7 +237,12 @@ export function createSampleDataTableScreen(): Screen { props: { _id: "c38f2b9f250fb4c33965ce47e12c02a80", _component: "@budibase/standard-components/container", - _styles: { normal: {}, hover: {}, active: {}, selected: {} }, + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, _children: [ { _id: "cf600445f0b0048c79c0c81606b30d542", @@ -247,13 +252,13 @@ export function createSampleDataTableScreen(): Screen { "--grid-desktop-col-start": 1, "--grid-desktop-col-end": 13, "--grid-desktop-row-start": 3, - "--grid-desktop-row-end": 21, + "--grid-desktop-row-end": 19, }, hover: {}, active: {}, selected: {}, }, - _instanceName: "Inventory - Table", + _instanceName: "Inventory table", _children: [], table: { label: "Inventory", @@ -262,8 +267,16 @@ export function createSampleDataTableScreen(): Screen { datasourceName: "Sample Data", }, columns: [ - { label: "Item Tags", field: "Item Tags", active: true }, - { label: "Purchase Date", field: "Purchase Date", active: true }, + { + label: "Item Tags", + field: "Item Tags", + active: true, + }, + { + label: "Purchase Date", + field: "Purchase Date", + active: true, + }, { label: "Purchase Price", field: "Purchase Price", @@ -272,7 +285,11 @@ export function createSampleDataTableScreen(): Screen { // eslint-disable-next-line no-template-curly-in-string "${{ [cf600445f0b0048c79c0c81606b30d542].[Purchase Price] }}", }, - { label: "Notes", field: "Notes", active: true }, + { + label: "Notes", + field: "Notes", + active: true, + }, { label: "Status", field: "Status", @@ -289,22 +306,276 @@ export function createSampleDataTableScreen(): Screen { }, ], }, - { label: "SKU", field: "SKU", active: true }, - { label: "Item ID", field: "Item ID", active: true }, - { label: "Created At", field: "Created At", active: false }, - { label: "Updated At", field: "Updated At", active: false }, - { label: "Item Name", field: "Item Name", active: true }, + { + label: "SKU", + field: "SKU", + active: true, + }, + { + label: "Item ID", + field: "Item ID", + active: true, + }, + { + label: "Created At", + field: "Created At", + active: false, + }, + { + label: "Updated At", + field: "Updated At", + active: false, + }, + { + label: "Item Name", + field: "Item Name", + active: true, + }, ], initialSortColumn: "Item ID", + allowAddRows: false, + allowEditRows: false, + allowDeleteRows: false, + stripeRows: true, + onRowClick: [ + { + parameters: { + key: "inventoryID", + type: "set", + value: "{{ [eventContext].[row]._id }}", + }, + "##eventHandlerType": "Update State", + id: "fgVuxCvjL", + }, + { + parameters: { + id: "c73fd03209dd44dd3937a33c6205b031d", + }, + "##eventHandlerType": "Open Side Panel", + id: "hwnlhdSUb", + }, + ], }, { _id: "c09edf7de69be44ce8f0215c3f62e43a5", _component: "@budibase/standard-components/textv2", - _styles: { normal: {}, hover: {}, active: {} }, - _instanceName: "New Text", + _styles: { + normal: { + "--grid-desktop-col-end": 3, + }, + hover: {}, + active: {}, + }, + _instanceName: "Table title", align: "left", text: "## Inventory", }, + { + _id: "c5879d3daffbd47619a833d3f88f07526", + _component: "@budibase/standard-components/button", + _styles: { + normal: { + "--grid-desktop-col-start": 11, + "--grid-desktop-col-end": 13, + "--grid-desktop-row-start": 1, + "--grid-desktop-row-end": 3, + "--grid-desktop-h-align": "end", + }, + hover: {}, + active: {}, + }, + _instanceName: "New row button", + text: "Create row", + type: "cta", + size: "M", + gap: "M", + onClick: [ + { + parameters: { + id: "c34d2b7c480144f3c800be15a62111d24", + }, + "##eventHandlerType": "Open Side Panel", + id: "rYTWHu7k0", + }, + ], + }, + { + _id: "c73fd03209dd44dd3937a33c6205b031d", + _component: "@budibase/standard-components/sidepanel", + _styles: { + normal: {}, + hover: {}, + active: {}, + }, + _instanceName: "Edit row side panel", + ignoreClicksOutside: false, + _children: [ + { + _id: "c3a5c8d0caf35410f8b75d6cb493ac693", + _component: "@budibase/standard-components/formblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + }, + _instanceName: "Edit row form block", + dataSource: { + label: "Inventory", + tableId: "ta_bb_inventory", + type: "table", + datasourceName: "Sample Data", + resourceId: "ta_bb_inventory", + }, + actionType: "Update", + buttonPosition: "bottom", + size: "spectrum--medium", + noRowsMessage: "We couldn't find a row to display", + disabled: false, + buttons: [ + { + text: "Save", + _id: "cc8c5d82717a54e68a610fe7204e25392", + _component: "@budibase/standard-components/button", + onClick: [ + { + "##eventHandlerType": "Validate Form", + parameters: { + componentId: "c3a5c8d0caf35410f8b75d6cb493ac693-form", + }, + }, + { + "##eventHandlerType": "Save Row", + parameters: { + providerId: "c3a5c8d0caf35410f8b75d6cb493ac693-form", + tableId: "ta_bb_inventory", + confirm: null, + }, + }, + { + "##eventHandlerType": "Close Screen Modal", + }, + { + "##eventHandlerType": "Close Side Panel", + }, + { + "##eventHandlerType": "Close Modal", + }, + ], + type: "cta", + }, + { + text: "Delete", + _id: "cf20dbe1df3d648599932b04f7e630376", + _component: "@budibase/standard-components/button", + onClick: [ + { + "##eventHandlerType": "Delete Row", + parameters: { + confirm: true, + tableId: "ta_bb_inventory", + rowId: + "{{ [c3a5c8d0caf35410f8b75d6cb493ac693-repeater].[_id] }}", + revId: + "{{ [c3a5c8d0caf35410f8b75d6cb493ac693-repeater].[_rev] }}", + }, + }, + { + "##eventHandlerType": "Close Screen Modal", + }, + { + "##eventHandlerType": "Close Side Panel", + }, + { + "##eventHandlerType": "Close Modal", + }, + ], + quiet: true, + type: "warning", + }, + ], + fields: null, + rowId: "{{ [state].[inventoryID] }}", + title: + "{{ [c3a5c8d0caf35410f8b75d6cb493ac693-repeater].[Item Name] }}", + }, + ], + }, + { + _id: "c34d2b7c480144f3c800be15a62111d24", + _component: "@budibase/standard-components/sidepanel", + _styles: { + normal: {}, + hover: {}, + active: {}, + }, + _instanceName: "New row side panel", + ignoreClicksOutside: false, + _children: [ + { + _id: "c61a1690c6ba0448db504eda38f766db1", + _component: "@budibase/standard-components/formblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + }, + _instanceName: "New Form Block", + dataSource: { + label: "Inventory", + tableId: "ta_bb_inventory", + type: "table", + datasourceName: "Sample Data", + resourceId: "ta_bb_inventory", + }, + actionType: "Create", + buttonPosition: "bottom", + size: "spectrum--medium", + noRowsMessage: "We couldn't find a row to display", + disabled: false, + buttons: [ + { + text: "Save", + _id: "ced8cf5175b9c40aabd216f23f072a44c", + _component: "@budibase/standard-components/button", + onClick: [ + { + "##eventHandlerType": "Validate Form", + parameters: { + componentId: "c61a1690c6ba0448db504eda38f766db1-form", + }, + }, + { + "##eventHandlerType": "Save Row", + parameters: { + providerId: "c61a1690c6ba0448db504eda38f766db1-form", + tableId: "ta_bb_inventory", + confirm: null, + }, + }, + { + "##eventHandlerType": "Close Screen Modal", + }, + { + "##eventHandlerType": "Close Side Panel", + }, + { + "##eventHandlerType": "Close Modal", + }, + { + "##eventHandlerType": "Clear Form", + parameters: { + componentId: "c61a1690c6ba0448db504eda38f766db1-form", + }, + }, + ], + type: "cta", + }, + ], + fields: null, + title: "Add to inventory", + }, + ], + }, ], _instanceName: "Inventory - List", layout: "grid", diff --git a/packages/server/src/sdk/app/rows/search/internal/sqs.ts b/packages/server/src/sdk/app/rows/search/internal/sqs.ts index 84162a67af..1e4218c614 100644 --- a/packages/server/src/sdk/app/rows/search/internal/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/internal/sqs.ts @@ -5,6 +5,8 @@ import { EnrichedQueryJson, FieldType, isLogicalSearchOperator, + LockName, + LockType, Operation, QueryJson, RelationshipFieldMetadata, @@ -30,6 +32,7 @@ import { } from "../../../tables/internal/sqs" import { context, + locks, sql, SQLITE_DESIGN_DOC_ID, SQS_DATASOURCE_INTERNAL, @@ -509,7 +512,14 @@ export async function search( } catch (err: any) { const msg = typeof err === "string" ? err : err.message if (!opts?.retrying && resyncDefinitionsRequired(err.status, msg)) { - await sdk.tables.sqs.syncDefinition() + await locks.doWithLock( + { + type: LockType.AUTO_EXTEND, + name: LockName.SQS_SYNC_DEFINITIONS, + resource: context.getAppId(), + }, + sdk.tables.sqs.syncDefinition + ) return search(options, source, { retrying: true }) } // previously the internal table didn't error when a column didn't exist in search diff --git a/packages/types/src/sdk/locks.ts b/packages/types/src/sdk/locks.ts index d455e95353..16173bd327 100644 --- a/packages/types/src/sdk/locks.ts +++ b/packages/types/src/sdk/locks.ts @@ -22,6 +22,7 @@ export enum LockName { QUOTA_USAGE_EVENT = "quota_usage_event", APP_MIGRATION = "app_migrations", PROCESS_USER_INVITE = "process_user_invite", + SQS_SYNC_DEFINITIONS = "sys_sync_definitions", } export type LockOptions = {