Skip to content

Commit b0d9f4e

Browse files
committed
remove module level cache, use syncContext between paginated calls to avoid redundant schema fetches
1 parent e7c143b commit b0d9f4e

File tree

3 files changed

+24
-11
lines changed

3 files changed

+24
-11
lines changed

apps/sim/connectors/airtable/airtable.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ function extractTitle(fields: Record<string, unknown>, titleField?: string): str
6464
return String(fields[candidate])
6565
}
6666
}
67-
// Fall back to first non-null string field
6867
for (const value of Object.values(fields)) {
6968
if (typeof value === 'string' && value.trim()) {
7069
return value.length > 80 ? `${value.slice(0, 80)}…` : value
@@ -136,16 +135,16 @@ export const airtableConnector: ConnectorConfig = {
136135
listDocuments: async (
137136
accessToken: string,
138137
sourceConfig: Record<string, unknown>,
139-
cursor?: string
138+
cursor?: string,
139+
syncContext?: Record<string, unknown>
140140
): Promise<ExternalDocumentList> => {
141141
const baseId = sourceConfig.baseId as string
142142
const tableIdOrName = sourceConfig.tableIdOrName as string
143143
const viewId = sourceConfig.viewId as string | undefined
144144
const titleField = sourceConfig.titleField as string | undefined
145145
const maxRecords = sourceConfig.maxRecords ? Number(sourceConfig.maxRecords) : 0
146146

147-
// Fetch table schema for field name mapping
148-
const fieldNames = await fetchFieldNames(accessToken, baseId, tableIdOrName)
147+
const fieldNames = await fetchFieldNames(accessToken, baseId, tableIdOrName, syncContext)
149148

150149
const params = new URLSearchParams()
151150
params.append('pageSize', String(PAGE_SIZE))
@@ -249,7 +248,6 @@ export const airtableConnector: ConnectorConfig = {
249248
}
250249

251250
try {
252-
// Verify base and table are accessible by fetching 1 record
253251
const encodedTable = encodeURIComponent(tableIdOrName)
254252
const url = `${AIRTABLE_API}/${baseId}/${encodedTable}?pageSize=1`
255253
const response = await fetchWithRetry(
@@ -274,7 +272,6 @@ export const airtableConnector: ConnectorConfig = {
274272
return { valid: false, error: `Airtable API error: ${response.status} - ${errorText}` }
275273
}
276274

277-
// If a view is specified, verify it exists
278275
const viewId = sourceConfig.viewId as string | undefined
279276
if (viewId) {
280277
const viewUrl = `${AIRTABLE_API}/${baseId}/${encodedTable}?pageSize=1&view=${encodeURIComponent(viewId)}`
@@ -352,13 +349,16 @@ async function recordToDocument(
352349

353350
/**
354351
* Fetches the table schema to build a field ID → field name mapping.
355-
* Falls back to an empty map if the schema endpoint is unavailable.
356352
*/
357353
async function fetchFieldNames(
358354
accessToken: string,
359355
baseId: string,
360-
tableIdOrName: string
356+
tableIdOrName: string,
357+
syncContext?: Record<string, unknown>
361358
): Promise<Map<string, string>> {
359+
const cacheKey = `fieldNames:${baseId}/${tableIdOrName}`
360+
if (syncContext?.[cacheKey]) return syncContext[cacheKey] as Map<string, string>
361+
362362
const fieldNames = new Map<string, string>()
363363

364364
try {
@@ -395,5 +395,6 @@ async function fetchFieldNames(
395395
})
396396
}
397397

398+
if (syncContext) syncContext[cacheKey] = fieldNames
398399
return fieldNames
399400
}

apps/sim/connectors/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,17 @@ export interface ConnectorConfig {
8484
/** Source configuration fields rendered in the add-connector UI */
8585
configFields: ConnectorConfigField[]
8686

87-
/** List all documents from the configured source (handles pagination via cursor) */
87+
/**
88+
* List all documents from the configured source (handles pagination via cursor).
89+
* syncContext is a mutable object shared across all pages of a single sync run —
90+
* connectors can use it to cache expensive lookups (e.g. schema fetches) without
91+
* leaking state into module-level globals.
92+
*/
8893
listDocuments: (
8994
accessToken: string,
9095
sourceConfig: Record<string, unknown>,
91-
cursor?: string
96+
cursor?: string,
97+
syncContext?: Record<string, unknown>
9298
) => Promise<ExternalDocumentList>
9399

94100
/** Fetch a single document by its external ID */

apps/sim/lib/knowledge/connectors/sync-engine.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,15 @@ export async function executeSync(
152152
let cursor: string | undefined
153153
let hasMore = true
154154
const MAX_PAGES = 500
155+
const syncContext: Record<string, unknown> = {}
155156

156157
for (let pageNum = 0; hasMore && pageNum < MAX_PAGES; pageNum++) {
157-
const page = await connectorConfig.listDocuments(accessToken, sourceConfig, cursor)
158+
const page = await connectorConfig.listDocuments(
159+
accessToken,
160+
sourceConfig,
161+
cursor,
162+
syncContext
163+
)
158164
externalDocs.push(...page.documents)
159165

160166
if (page.hasMore && !page.nextCursor) {

0 commit comments

Comments
 (0)