Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
28978d1
Encode rows in PostgreSQL's binary COPY wire format
sduchesneau Aug 12, 2026
8ec16c6
Hold rows on disk and apply whole segments in the background
sduchesneau Aug 12, 2026
17362ae
Write through the spool, load without constraints
sduchesneau Aug 12, 2026
daa7737
Cover the spool, the write modes and the constraint timings
sduchesneau Aug 12, 2026
8d5c16b
Measure what the load actually costs
sduchesneau Aug 12, 2026
2e999da
Document the flags, the write modes and the constraints
sduchesneau Aug 12, 2026
2d6fa36
Drain the spool before creating constraints at the stop block
sduchesneau Aug 12, 2026
d25be1f
Say at startup which constraints the schema is missing
sduchesneau Aug 12, 2026
6309fe6
Create constraints one transaction at a time
sduchesneau Aug 12, 2026
1ad8981
Let the schema commands be told which module
sduchesneau Aug 12, 2026
5f0f029
Say when a package has no annotations to derive keys from
sduchesneau Aug 12, 2026
a7f9d08
Index _block_number_, and scaffold the annotations
sduchesneau Aug 12, 2026
0ef8061
Measure what the block number index costs and buys
sduchesneau Aug 13, 2026
a3b0aaa
Create the block number index at startup, concurrently
sduchesneau Aug 13, 2026
7636edb
Merge remote-tracking branch 'origin/develop' into feature/sink-sql-l…
sduchesneau Aug 13, 2026
c15e379
fix bad log reason when draining spool for 'reached end block'
sduchesneau Aug 13, 2026
d39ca7b
fix COPY mode not applying 'bytes-encoding'
sduchesneau Aug 13, 2026
2813a16
support enum values in clickhouse
sduchesneau Aug 13, 2026
6b0a6dd
remove 'disable-...' flags on "constraints drop" subcommand
sduchesneau Aug 13, 2026
8d45b63
fix conversion of numeric repeated-strings in COPY mode
sduchesneau Aug 13, 2026
fe6aa2a
sink clickhouse without proto annotations
sduchesneau Aug 13, 2026
1fb4eaa
add engine suffix to the sink user agent
sduchesneau Aug 13, 2026
ded38bd
drop --live-block-time-delta from the SQL sink
sduchesneau Aug 13, 2026
b81894f
add some comments to explain behavior of undo and cursors regarding s…
sduchesneau Aug 13, 2026
d339a97
Keep the spool within the budget it was given
sduchesneau Aug 13, 2026
e347d58
Close the COPY stream's file when a segment is sealed
sduchesneau Aug 13, 2026
9adefd3
Let a cyclic schema undo a reorg
sduchesneau Aug 13, 2026
1ded630
Write the ClickHouse cursor to its file when a segment lands
sduchesneau Aug 13, 2026
5c22716
Route ClickHouse block rows through the spool
sduchesneau Aug 13, 2026
0b75225
Stop doubling backslashes in rendered SQL literals
sduchesneau Aug 13, 2026
529d13f
Encode an empty repeated field in COPY mode
sduchesneau Aug 13, 2026
6d11882
Encode a repeated enum field in COPY mode
sduchesneau Aug 13, 2026
6474c8e
Stop dialing the ClickHouse database before creating it
sduchesneau Aug 13, 2026
fa3c73b
Match a recovered segment on both ends of its range
sduchesneau Aug 13, 2026
277e9c0
Size the next segment from the throughput actually measured
sduchesneau Aug 13, 2026
5fade48
Have setup create the constraints it is asked for
sduchesneau Aug 13, 2026
0bb497f
Seal the spool on every way out of the run
sduchesneau Aug 13, 2026
ae9036b
Write a proto that parses from extract-proto --sql
sduchesneau Aug 13, 2026
1d801df
Reconcile the COPY array fixes with feature/sink-sql-high-fixes
sduchesneau Aug 14, 2026
e6f4060
Seal a segment only once its own cursor is recorded
sduchesneau Aug 14, 2026
002aa52
Clear the applied-segment records at the chain head
sduchesneau Aug 14, 2026
a6bf758
Bound a framed record by its file rather than a constant
sduchesneau Aug 14, 2026
c7b45d2
Fix the spool's critical bugs before it ships (#881)
sduchesneau Aug 14, 2026
371fde7
Leave the constraints alone at a stop block
sduchesneau Aug 14, 2026
7dbf1f5
Let setup say when its constraints are created
sduchesneau Aug 14, 2026
6b78c07
Warn on the spool's budget rather than on a block count
sduchesneau Aug 14, 2026
d984d28
Build the constraints in parallel
sduchesneau Aug 14, 2026
4f50d3e
Bring the change log in line with the constraint pass
sduchesneau Aug 14, 2026
fb92889
Merge remote-tracking branch 'origin/develop' into feature/sink-sql-l…
sduchesneau Aug 14, 2026
5478b13
Bump golang.org/x/mod to v0.40.0 for CVE-2026-56865
sduchesneau Aug 15, 2026
39ce2df
move changes from sink_sql_common to simplify changes
sduchesneau Aug 17, 2026
4f0a1c3
change naming in docs/helps: from-proto -> Relational Mappings
sduchesneau Aug 17, 2026
7ac5bb0
Fix TestPartialBlocksWithStores and 'devenv' helper tool
sduchesneau Aug 17, 2026
d1de5aa
tweak changelog, adjust user-agent string on sinks
sduchesneau Aug 17, 2026
f9278ad
bump changelog
sduchesneau Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions bin/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ main() {
set -e

go test ./... "$@"
# commented while they don't work on github for now

# tests_e2e is its own Go module, so `go test ./...` above never reaches it. It stays
# out of this script until it passes on Linux: the node writes root-owned files into
# the bind-mounted t.TempDir, which fails every container test's cleanup on a CI
# runner. See the fix/tests-e2e-on-linux branch.
#
# pushd tests_e2e &> /dev/null
# go test ./... "$@"
# popd
# popd &> /dev/null
Comment on lines +22 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Useless, still not running ...

}

usage_error() {
Expand Down
2 changes: 1 addition & 1 deletion cmd/substreams/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package main
import (
"testing"

pbconvo "github.com/streamingfast/substreams/pb/sf/codegen/conversation/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
pbconvo "github.com/streamingfast/substreams/pb/sf/codegen/conversation/v1"
)

func TestProtocolOrderPreservation(t *testing.T) {
Expand Down
24 changes: 14 additions & 10 deletions cmd/substreams/sink_clickhouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ var sinkClickhouseCmd = &cobra.Command{
}

var sinkClickhouseSetupCmd = &cobra.Command{
Use: "setup <manifest>",
Use: "setup <manifest> [<module>]",
Short: "Setup the required infrastructure to deploy a Substreams SQL deployable unit",
Long: cli.Dedent(`
Setup the database for the Substreams SQL sink, auto-detecting the mode from the
output module type, exactly like the run action:

- DatabaseChanges output: creates the system tables (cursors, history) and applies
the 'schema.sql' bundled in the manifest sink config.
- Any other output type (from-proto): resolves the schema from the module's output
proto and creates the database schema and tables, then exits. This step is
idempotent and can be run again safely.
- Database Changes Mode ('DatabaseChanges' output): creates the system tables
(cursors, history) and applies the 'schema.sql' bundled in the manifest sink
config.
- Relational Mappings Mode (any other output type): resolves the schema from the
module's output proto and creates the database schema and tables, then exits.
This step is idempotent and can be run again safely.
`),
Args: cobra.ExactArgs(1),
Args: cobra.RangeArgs(1, 2),
RunE: newSinkSetupE(sinkClickhouseDriver),
}

Expand All @@ -39,15 +40,18 @@ func init() {
addOperatorFlags(persistent)

addSinkRunFlags(sinkClickhouseCmd.Flags(), sinkClickhouseDriver)
setModeGroupedUsage(sinkClickhouseCmd)

setupFlags := sinkClickhouseSetupCmd.Flags()
addCursorTableFlags(setupFlags)
addClusterFlag(setupFlags)
addOnModuleHashMismatchFlag(setupFlags)
setupFlags.Bool("system-tables-only", false, "[DatabaseChanges mode] will only create/update the systems tables (cursors, substreams_history) and ignore the schema from the manifest")
setupFlags.Bool("ignore-duplicate-table-errors", false, "[DatabaseChanges mode][Dev] Use this if you want to ignore duplicate table errors, take caution that this means the 'schema.sql' file will not have run fully!")
setupFlags.Bool("system-tables-only", false, "will only create/update the systems tables (cursors, substreams_history) and ignore the schema from the manifest")
setupFlags.Bool("ignore-duplicate-table-errors", false, "[Dev] Use this if you want to ignore duplicate table errors, take caution that this means the 'schema.sql' file will not have run fully!")
addBytesEncodingFlag(setupFlags)
addFromProtoModeRunFlags(setupFlags, sinkClickhouseDriver)
addFromProtoSchemaFlags(setupFlags)
addConstraintTimingFlag(setupFlags)
addClickhouseStateFlags(setupFlags)

sinkClickhouseCmd.AddCommand(sinkClickhouseSetupCmd)
sinkClickhouseCmd.AddCommand(newSinkToolsCmd(sinkClickhouseDriver))
Expand Down
93 changes: 82 additions & 11 deletions cmd/substreams/sink_postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,110 @@ var sinkPostgresCmd = &cobra.Command{
}

var sinkPostgresSetupCmd = &cobra.Command{
Use: "setup <manifest>",
Use: "setup <manifest> [<module>]",
Short: "Setup the required infrastructure to deploy a Substreams SQL deployable unit",
Long: cli.Dedent(`
Setup the database for the Substreams SQL sink, auto-detecting the mode from the
output module type, exactly like the run action:

- DatabaseChanges output: creates the system tables (cursors, history) and applies
the 'schema.sql' bundled in the manifest sink config.
- Any other output type (from-proto): resolves the schema from the module's output
proto and creates the database schema and tables, then exits. This step is
idempotent and can be run again safely.
- Database Changes Mode ('DatabaseChanges' output): creates the system tables
(cursors, history) and applies the 'schema.sql' bundled in the manifest sink
config.
- Relational Mappings Mode (any other output type): resolves the schema from the
module's output proto and creates the database schema and tables, then exits.
This step is idempotent and can be run again safely.
`),
Args: cobra.ExactArgs(1),
Args: cobra.RangeArgs(1, 2),
RunE: newSinkSetupE(sinkPostgresDriver),
}

var sinkPostgresConstraintsCmd = &cobra.Command{
Use: "constraints",
Short: "Create or drop the schema's constraints on an already loaded database",
}

var sinkPostgresConstraintsApplyCmd = &cobra.Command{
Use: "apply <manifest> [<module>]",
Short: "Create the schema's constraints on an already loaded database",
Long: cli.Dedent(`
Create the primary keys, unique and foreign key constraints of a Relational
Mappings schema on a database the sink has already loaded, skipping the ones
already in place.

The sink loads without them on purpose: measured through binary COPY, loading with
foreign keys in place runs 27x slower than loading without, where building the very
same constraints afterwards costs 3.3x. Creating them is a stop-the-world
operation, though — every index is built and every foreign key validated, with the
tables locked while it runs — so on a large database this belongs in a maintenance
window, which is what --apply-constraints=manual leaves it to this command for.

The index on _block_number_ is not created here. The sink creates that one when it
starts, concurrently: this command is yours to schedule, and the reorg path cannot
wait for a maintenance window.

Running it again is safe: constraints already in place are left alone.

The module is inferred from the package when it is left out. A package with more
than one candidate has to be told which, or the schema this derives will not be the
one the run created.
`),
Args: cobra.RangeArgs(1, 2),
RunE: newSinkConstraintsE(sinkPostgresDriver, constraintsApply),
}

var sinkPostgresConstraintsDropCmd = &cobra.Command{
Use: "drop <manifest> [<module>]",
Short: "Drop the schema's constraints",
Long: cli.Dedent(`
Drop the primary keys, unique and foreign key constraints of a Relational Mappings
schema, leaving anything the sink did not create alone — the index on _block_number_
included, that one being the sink's own and recreated when it next starts.

This is the escape hatch after --apply-constraints=always, and what makes a
backfill that has to be resumed fast again without setting the schema up afresh:
loading with foreign keys in place measured 27x slower than loading without them.

Running it again is safe: anything already absent is skipped.
`),
Args: cobra.RangeArgs(1, 2),
RunE: newSinkConstraintsE(sinkPostgresDriver, constraintsDrop),
}

func init() {
persistent := sinkPostgresCmd.PersistentFlags()
addDSNFlag(persistent)
addOperatorFlags(persistent)

addSinkRunFlags(sinkPostgresCmd.Flags(), sinkPostgresDriver)
setModeGroupedUsage(sinkPostgresCmd)

setupFlags := sinkPostgresSetupCmd.Flags()
addCursorTableFlags(setupFlags)
addOnModuleHashMismatchFlag(setupFlags)
setupFlags.Bool("postgraphile", false, "[DatabaseChanges mode] Will append the necessary 'comments' on cursors table to fully support postgraphile")
setupFlags.Bool("system-tables-only", false, "[DatabaseChanges mode] will only create/update the systems tables (cursors, substreams_history) and ignore the schema from the manifest")
setupFlags.Bool("ignore-duplicate-table-errors", false, "[DatabaseChanges mode][Dev] Use this if you want to ignore duplicate table errors, take caution that this means the 'schema.sql' file will not have run fully!")
setupFlags.Bool("postgraphile", false, "Will append the necessary 'comments' on cursors table to fully support postgraphile")
setupFlags.Bool("system-tables-only", false, "will only create/update the systems tables (cursors, substreams_history) and ignore the schema from the manifest")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why we lost [DatabaseChanges] note, if it's because it's not adding system tables in both, we should list all possible system tables for both mode.

setupFlags.Bool("ignore-duplicate-table-errors", false, "[Dev] Use this if you want to ignore duplicate table errors, take caution that this means the 'schema.sql' file will not have run fully!")
addBytesEncodingFlag(setupFlags)
addFromProtoModeRunFlags(setupFlags, sinkPostgresDriver)
addFromProtoSchemaFlags(setupFlags)
addConstraintTimingFlag(setupFlags)

applyFlags := sinkPostgresConstraintsApplyCmd.Flags()
addBytesEncodingFlag(applyFlags)
addFromProtoSchemaFlags(applyFlags)
addConstraintPassFlags(applyFlags)

// Drop always removes every constraint managed by the sink. The disable-* and
// --no-constraints flags describe what should be created, so exposing them here would
// suggest that drop honors a policy it cannot apply.
dropFlags := sinkPostgresConstraintsDropCmd.Flags()
addBytesEncodingFlag(dropFlags)
dropFlags.String("proto-file-override", "", "Override protobuf file to use instead of extracting from substreams package")
addConstraintPassFlags(dropFlags)
sinkPostgresConstraintsCmd.AddCommand(sinkPostgresConstraintsApplyCmd)
sinkPostgresConstraintsCmd.AddCommand(sinkPostgresConstraintsDropCmd)

sinkPostgresCmd.AddCommand(sinkPostgresSetupCmd)
sinkPostgresCmd.AddCommand(sinkPostgresConstraintsCmd)
sinkPostgresCmd.AddCommand(newSinkToolsCmd(sinkPostgresDriver))

SinkCmd.AddCommand(sinkPostgresCmd)
Expand Down
2 changes: 1 addition & 1 deletion cmd/substreams/sink_postgres_generate_csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func sinkPostgresGenerateCSVE(cmd *cobra.Command, args []string) error {
supportedOutputTypes,
manifestPath,
outputModule,
"sink_database_changes",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yeah!

sinkUserAgent("sink_database_changes", sinkPostgresDriver),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sink_database_changes and even sinkUserAgent don't really use more "standard" agent formats which uses a lot the / for namespacing.

We should do the same + show full name postgres, clickhouse.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should also embedded CLI version in the user agent too

zlog,
tracer,
)
Expand Down
Loading
Loading