Chapter 11: postjevsql-sidecar/src, config, secrets and a plan

Everything the sidecar decides, with nothing that talks to a database. Three files, each one a rule you can test with plain cargo test.

One config file. config.rs reads the TOML every launcher writes:

[target]
server = "target"          # the foreign server's local name (the default)
host = "db.example.com"
port = 5432                # the default
dbname = "app"
sslmode = "verify-full"    # the default
use_remote_estimate = true # the default
schemas = ["public"]       # imported into same-named local schemas

[[mapping]]
local_user = "app"
remote_user = "app_ro"
password_file = "/run/secrets/app_ro"  # or $POSTJEVSQL_TARGET_PASSWORD

[jev]
api_key_file = "/run/secrets/typesafe" # or $TYPESAFE_API_KEY

It also takes sslrootcert, fetch_size (default 100, the scan's in-flight window, so each window is one round trip), and a mapping's password_env. An unknown key is an error, not a typo quietly ignored.

Every secret from exactly one place. secret.rs resolves each secret from a file or an env var. Both set is an error, because a precedence rule would silently use the wrong credential, and so is neither, for a secret the command needs. (A mapping with no password at all is how PG18's use_scram_passthrough is asked for.) An empty file or variable is an error too. The error names the file and the variable, never the value.

A plan, not a rebuild. sync.rs compares the target's tables with the sidecar's foreign tables and plans the changes, one at a time: create a table, drop one, add, drop or rename a column, change a type or a NOT NULL. It never drops a table to import it again, because the sidecar's own views, grants and functions over a foreign table would go with it.

flowchart LR
  T["the target's catalog"] --> P["sync::plan"]
  L["the sidecar's foreign tables"] --> P
  P --> C["CreateTable · DropTable · AddColumn · DropColumn · RenameColumn · AlterType · SetNotNull"]

Aside: how a rename is told from a drop. A column is matched by its remote name. When exactly one column disappeared and one appeared at the same position, with the same type and nullability, that is a rename. Anything else is a drop and an add, and the CLI's dependency check refuses the drop if a view uses the column. Guessing generously here would rename the wrong thing; guessing stingily only ever refuses.

Try it. cargo test -p postjevsql-sidecar runs these files' tests in a fraction of a second.

For the people who maintain it

FileWhat
lib.rsThe three modules.
config.rsConfig, Target, SslMode (verify-full by default; the weaker modes are for loopback tests), Mapping, Jev.
secret.rsSources and Source: a secret from a file or an env var, exactly one, and SecretError, which names the sources.
sync.rsTable, Column, Change, Touches (what a change can break), plan, and ident/lit for quoting.

← Previous: Chapter 10, postjevsql-sidecar/ · Up: postjevsql-sidecar · Next: Chapter 12, cli/ →