dynamic_table_check() function.
This feature can be used with a Goldsky Hosted Postgres database, or with your own Postgres database, allowing for clean compliance with GDPR and other data protection laws.
Key Features
- Real-time Updates: Add or remove values without pipeline restart by updating a postgres table.
- Unbounded Filters: Filter on unlimited values from onchain data or backend updates.
- High Throughput: Sub-microsecond lookups, with dynamically batched queries for performance.
- Pipeline Updates: Update the table with on-chain data in the same pipeline.
Use Cases
Wallet Tracking
Monitor transfers to/from specific wallet addresses
Deduplication
Track processed records to avoid duplicates
Factory Pattern
Track contracts created by a factory
Basic Configuration
Parameters
string
required
Must be
dynamic_tablestring
required
Storage backend:
Postgresstring
required
The table name in the backend storage. For Postgres, this creates a table in
the
streamling schema (configurable via the schema field).string
required
The name of a Goldsky secret containing Postgres credentials. Required for
the Postgres backend.
string
Optional. SQL query to automatically populate the table from pipeline data.
Must be a single projection/filter (
SELECT ... FROM ... WHERE ...; no
subqueries, joins, or aggregations) reading directly from a source;
when the table is consumed via dynamic_table_check, it is planned before
the pipeline’s transforms, so its SQL cannot reference a transform.string
Optional. PostgreSQL schema name for the table. Defaults to
streamling.string
Optional. Name of the primary key column storing values. Defaults to
value.string
Optional. Name of the timestamp column. Defaults to
updated_at. The
in-memory cache uses this column to find new rows.boolean
Optional. Set to
true to load the table into memory and serve lookups from
the cache instead of querying Postgres for each batch. Defaults to false.
See In-memory caching.integer
Optional. How often, in milliseconds, the pipeline checks Postgres for new
rows. Only set this field when
cache is true. Defaults to 1000.Backend Types
PostgreSQL Backend (Recommended)
Best for production deployments requiring persistence:- Data persists across pipeline restarts and failures
- Can be updated externally via direct SQL, no redeploy needed
- Indexed primary-key lookups scale to millions of rows
- A primary key column (default:
value) storing the lookup values - A timestamp column (default:
updated_at) automatically set to the insertion time
streamling schema: streamling.tracked_contracts
Custom schema and column names
You can customize the schema, column name, and timestamp column name:my_app.tracked_contracts:
In-memory caching
By default, everydynamic_table_check() call sends a lookup query to Postgres for each batch. With caching enabled, the pipeline loads the whole table into memory once at startup and serves lookups from memory. This removes the per-batch database round trip, which lowers latency on busy pipelines.
After startup, the pipeline checks MAX(time_column) on an interval set by cache_refresh_debounce_ms (default: once per second). It then loads only the rows newer than the last value it saw.
To enable caching, set cache: true and make sure time_column points to the table’s timestamp column:
cache defaults to false. Only set cache_refresh_debounce_ms when cache is true.
Requirements for cached tables
The cache only adds rows. It never re-reads rows it has already loaded. Follow these rules when you write to a cached table:1
Set an always-increasing timestamp on every insert
Every insert must set Do not use
time_column, and its value must always increase. Use clock_timestamp(), for example as the column default. The table Goldsky creates automatically uses NOW(), so create the table yourself before you deploy the pipeline:NOW() or CURRENT_TIMESTAMP. Both are fixed when the transaction starts, so a row can commit with a timestamp older than one the pipeline has already seen. The pipeline never loads that row.2
Serialize writes from multiple processes
If more than one process writes to the table, each insert must first take a transaction-level advisory lock on the table in the same transaction:Replace
streamling.tracked_wallets with your <schema>.<table>.3
Restart the pipeline to apply removals and updates
The cache does not pick up deleted or updated rows. If you delete a row, for example when you stop tracking a wallet, the pipeline keeps matching it until you restart the pipeline:Turbo pipelines restart quickly. If you need removals to take effect right away, leave caching off.
PostgreSQL setup
Dynamic tables with a Postgres backend need a database Goldsky can connect to and a secret with valid credentials. Use the same secret for yourdynamic_table transform and any Postgres sinks in the same pipeline.
- Goldsky hosted PostgreSQL
- IP allowlisted PostgreSQL
- Public PostgreSQL
Goldsky can provision and manage a PostgreSQL database for you. This is the fastest path for dynamic tables: no IP allowlisting and no database administration on your side.Follow Goldsky-hosted Postgres on the Turbo Postgres sink page to provision a database from the web app and store the connection string as a Goldsky secret. Reference that secret in your You can reuse the same secret for Postgres sinks in the same pipeline. After deploy, update lookup values directly in SQL (default table location:
dynamic_table transform:streamling.<backend_entity_name>).Verify the connection
After deployment, confirm the dynamic table was created:- Connection refused or timeout: Firewall, wrong host/port, or missing IP allowlist entry (use the IP allowlisted tab if your database blocks unknown IPs).
- Permission denied: The database role lacks
CREATEon the database orUSAGE/CREATEon the target schema. - SSL required: Add
?sslmode=requireto your connection string for providers that enforce TLS.
Using Dynamic Tables in SQL
Once defined, use thedynamic_table_check() function in SQL transforms:
Function Signature
- table_name (
TEXT): The transform name of the dynamic table in your pipeline (not thebackend_entity_name). Must be a string literal: the same value on every row. - value (
TEXT): The value to check for existence. - Returns:
trueif the value exists in the table,falseotherwise.
dynamic_table_check in SQL functions reference.
Auto-Population with SQL
You can automatically populate a dynamic table from your pipeline data:When using SQL to populate, the query only supports projections and filters
(no joins or aggregations).
Manual Updates
For Postgres backends, you can update the table directly using any Postgres client. (Substitute the schema and column names if you customized them.)Example: Track Specific Token Contracts
Monitor transfers for specific ERC-20 tokens like USDC:Example: Track Wallet Activity
Monitor all ERC-20 transfers for specific wallets:Example: Complete Pipeline with Dynamic Tables
This example shows a complete pipeline that uses dynamic tables to filter ERC-20 transfers to specific contracts:Example: Factory Pattern
Track all contracts created by a factory and filter events from those contracts:Source Validation
Good example:Performance Considerations
Lookup Performance
Lookup Performance
- Lookups are batched and executed in parallel against Postgres (
ANY(ARRAY[...])queries). - The value column is
PRIMARY KEY, so Postgres uses its unique index automatically. - Large tables (millions of entries) work fine as long as the Postgres instance has adequate resources.
Update Latency
Update Latency
- Postgres backend: by default, each
dynamic_table_check()call queries the table directly, so changes take effect on the next batch, typically within a second or two. - With in-memory caching enabled, new rows take effect after the next cache refresh (
cache_refresh_debounce_ms, default 1000 ms). Deletes and updates require a pipeline restart. - Auto-population via SQL: updates flow through with normal pipeline latency.
Table Size
Table Size
- No hard row limit is enforced, but lookup cost scales with table size, so keep tables as small as your use case allows.
- Use specific filters in auto-population SQL to avoid unbounded growth.
- For long-running pipelines, consider a cleanup strategy (
DELETEold rows byupdated_at).
Best Practices
1
Use Postgres for production
Always use the Postgres backend for production deployments to ensure data persistence and external updatability.
2
Use specific filters in auto-population SQL
Use specific filters in auto-population SQL to ensure data consistency and proper synchronization.
3
Lowercase string values
Store addresses and other identifiers in lowercase for consistent matching:
4
Monitor table growth
Periodically check table sizes and clean up old entries by
updated_at if needed.