Skip to content

docs: document NetSuite sync modes, delete handling, and table types#2667

Open
jwhartley wants to merge 1 commit intomasterfrom
docs/netsuite-sync-modes-deletes
Open

docs: document NetSuite sync modes, delete handling, and table types#2667
jwhartley wants to merge 1 commit intomasterfrom
docs/netsuite-sync-modes-deletes

Conversation

@jwhartley
Copy link
Contributor

Summary

  • Add "Sync modes and data loading" sections to both NetSuite connector docs (SuiteAnalytics and SuiteQL)
  • Explain how incremental vs full refresh is determined (presence of log_cursor / lastmodifieddate)
  • Document delete handling via NetSuite's DeletedRecord system table, including which table types support it (base tables) and which don't (linking/junction tables)
  • Clarify the distinction between paginated backfill, snapshot backfill, and scheduled full refresh
  • Add tip for switching a table from full refresh to incremental

Context

Customer conversations revealed that none of this behavior was documented. The docs previously listed configuration properties but didn't explain the resulting sync behavior, which tables get deletes, or how to check/change sync modes.

Test plan

  • Verify Docusaurus renders the new sections correctly (headings, tip box, internal links)
  • Joseph to review for technical accuracy against connector implementation

🤖 Generated with Claude Code

Add "Sync modes and data loading" sections to both NetSuite connector
docs (SuiteAnalytics and SuiteQL) explaining how incremental vs full
refresh is determined, how deletes are captured via DeletedRecord, and
the distinction between base tables and linking/junction tables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jwhartley jwhartley requested a review from jshearer February 9, 2026 03:33
@github-actions
Copy link

github-actions bot commented Feb 9, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://estuary.github.io/flow/pr-preview/pr-2667/

Built to branch gh-pages at 2026-02-09 03:35 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copy link
Contributor

@jshearer jshearer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly the SuiteQL information here is factually incorrect. If you don't already have access to the netsuite repo just make an access request and Mike or I can add you, there's a lot of valuable data in there about the history of the connectors, decisions we made in PR/commit messages etc.


## Sync modes and data loading

Each table binding uses one of two sync modes, determined by the cursor fields available on the table.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will come back after reading the whole thing, but... I would say the connector has at least 3 sync modes: snapshots, periodic backfills, and incremental replication.


### Full refresh

Tables without a `log_cursor` are synced via **full refresh** — the connector re-reads the entire table on each sync. This is common for linking and junction tables (e.g., `TransactionLine`, `NextTransactionLineLink`) that lack a `lastmodifieddate` column.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/estuary/source-netsuite/pull/193 will make these referenced tables out of date

There are two full refresh modes:

- **Paginated backfill** — Uses the [`page_cursor`](#bindings) to read the table in ordered pages. Pair this with a [`schedule`](#setting-a-schedule) cron expression to control how often the full refresh runs (e.g., daily).
- **Snapshot backfill** — Set [`snapshot_backfill: true`](#bindings) when no good page cursor exists and the table is small enough for a single query. Snapshot mode manages its own schedule via the `interval` field. Do **not** combine `snapshot_backfill` with a cron `schedule` — this will cause issues with delete emission.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth noting that periodic snapshots using interval are capable of capturing deletions on tables that are not covered by DeletedRecord


### Table associations

Linking tables can optionally be loaded as **associations** of a parent table instead of — or in addition to — standalone bindings. For example, `TransactionAccountingLine` can be associated with `Transaction` so that when a transaction is modified, its related accounting lines are also loaded.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I... have been somewhat hesitant to actually talk about this feature because of the major footgun that unless every change in the child table is reflected in an update to the parent table's last-modified cursor (which is almost never the case), using this feature will only appear to work, but actually miss a bunch of change events.

If you need to capture a table that is not yet supported, [contact support](mailto:support@estuary.dev) with the table name(s).
Estuary support will be able to confirm availability and, if needed, add the table(s) to the connector.

## Sync modes and data loading
Copy link
Contributor

@jshearer jshearer Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SuiteQL does not support incremental sync, deletion tracking (except via snapshots, those still capture deletes via _meta/row_id), associations, etc. This was an intentional decision we made to simplify the implementation given the constraints. Here's a comparison table that I had Claude put together. Are you in the netsuite connector repo? We should probably get you in there so you (or your agents ;)) can poke around.


Fundamental SuiteQL API Constraints That Drove the Design

These four API-level constraints shaped every design decision:

  1. No table metadata introspection - SuiteQL has no equivalent of OA_TABLES/OA_COLUMNS/OA_FKEYS. You cannot programmatically discover schemas, column types, primary keys, or foreign keys. The connector requires manual table/key specification via EndpointConfig.tables, with defaults from a small hints.py (19 lines, 10 tables).

  2. 100-column silent failure - SuiteQL silently returns zero results if a table exceeds 100 columns. Users can specify explicit columns lists, or use SELECT * with the understanding it fails silently on wide tables.

  3. 100k row result limit - SuiteQL caps results at 100,000 rows per query. The connector uses rownum-based subquery pagination (wrap_in_paged_subquery), but each subsequent page re-queries already-seen data, making it progressively slower. Practical ceiling is low single-digit millions of rows.

  4. Datetime information loss - SuiteQL returns datetime columns as date-only strings. Previous attempts to use TO_CHAR() conversions caused timezone mismatch bugs, and including many date-to-datetime conversions in a single query also causes failures. The new connector accepts this and returns data as-is, without hour/minute/second information.

Features SuiteQL Does NOT Support vs SuiteAnalytics

Capability SuiteAnalytics SuiteQL
Incremental replication Yes (via lastmodifieddate cursor fields) No. Full refreshes only.
Automatic schema discovery Yes (queries OA_COLUMNS with types, PKs, FKs) No. Manual table/key config only.
Deletion tracking Yes (DeletedRecord table + tombstones) No. Full refresh overwrites.
Associated document loading Yes (child rows loaded with parent changes) No.
Maximum columns supported 1,000 100
Concurrent backfill chunks Yes (parallel cursor-range splitting) No. Single-threaded only.
Typed schema models Yes (full Pydantic models with correct types) No. All fields are passed through from the API response, relies on schema inference.
Boolean normalization Yes (T/F to true/false) No. Data as-is.
Datetime with timezone Yes (UTC assumption, proper datetime types) No. Dates only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants