Multi-Region, Multi-Account Snowflake

If your exec asks "when us-east-1 goes dark, how fast can Finance be back online, and who can still read PII?" the right answer to a multi-region, multi-account Snowflake footprint is designed, not guessed. This guide lays out the real options - data sharing vs. replication vs. failover - and how they map to RPO/RTO and access-control requirements without lighting your spend on fire.

Why it matters

  • Multi-region gives regulatory coverage, latency control, and disaster recovery.
  • Multi-account gives strong blast-radius boundaries, autonomy for BU/geo teams, and least-privilege governance.
  • The hard part is choosing between live sharing, periodic replication, and promotable failover (per database or as grouped objects) while keeping RPO/RTO and RBAC honest.

The building blocks

1) Cross-account data sharing (direct share / listings)

  • What it is: a provider grants read-only access to specific objects to a consumer account (same or different region/cloud).
  • Why: zero-ETL distribution to many consumers; no copy jobs; consumers query live provider data.
  • RPO/RTO: effectively RPO ≈ 0 for published objects; RTO ≈ 0 for read queries (no promotion step).
  • Caveat: no write access on the consumer; not a DR solution for writes.

Governance fit: tight, object-level grants; overlay row access / masking for PII; create separate shares per audience.

2) Database replication (primary → secondary)

  • What it is: Snowflake replicates a read-only secondary database in a target account/region.
  • Why: off-region analytics copies; warm standby for DR; minimize egress and operational glue.
  • RPO/RTO: RPO = replication interval (minutes to hours); RTO = promotion time (seconds to minutes).
  • Caveat: the secondary is read-only until you promote it. Writes resume after switchover/failover.

3) Failover (promote secondary → primary)

  • What it is: turn a replicated secondary into the new primary (planned switchover or unplanned failover).
  • Why: true continuity for write workloads when a region/account is impaired.
  • RPO/RTO: driven by your replication schedule (RPO) and the promotion + connection cutover time (RTO).

4) Grouping objects for org-wide DR

  • What it is: treat multiple databases + grants + other selected objects as a unit so they replicate and fail over together, avoiding "oops, forgot that database" incidents.
  • Why: consistency across apps that span several databases and shared objects.

If your DR plan requires engineers to remember five manual steps under stress, you don't have a DR plan - you have a fantasy. Group things.

Choosing the right pattern

Use casePatternRPORTONotes
Share curated facts to 10+ consumer accountsData Sharing~0~0Perfect for distribution; not for write continuity
Analytics read copies closer to users/BIDatabase ReplicationMinutes - HoursMinutesConsumers read from secondary; primary continues writes
Full DR for write workloadsReplication + FailoverMinutesMinutesTest switchover quarterly; automate connection flips
Multi-tenant BUs with strict isolationMulti-Account + Sharing~0~0Each BU owns compute; central team curates datasets
Regulated PII with geo boundariesPer-region primaries + sharing by region~0~0Overlay row/mask policies per jurisdiction

Reference architectures

  • Hub-and-spoke (most common): a central hub account in Region A publishes shares to regional spokes for read workloads. Critical write databases replicate to a paired DR account in Region B with a defined failover runbook.
  • Active/active read, active/passive write: reads served locally via replicated secondaries in each region; a single write primary with failover rights to the secondary region.
  • Producer → exchange → consumers: producers publish via listings/shares to many consumers (partners, BUs). DR for the producer is handled by replication/failover; consumers are unaffected.

Practical SQL snippets

You need appropriate org-level/admin privileges. Exact options vary by account/edition - treat these as patterns.

A) Share data cross-account (read-only)

-- Provider account
CREATE SHARE sales_share;
GRANT USAGE ON DATABASE SALES TO SHARE sales_share;
GRANT USAGE ON SCHEMA SALES.MART TO SHARE sales_share;
GRANT SELECT ON ALL TABLES IN SCHEMA SALES.MART TO SHARE sales_share;

-- Include future tables to avoid drift
GRANT SELECT ON FUTURE TABLES IN SCHEMA SALES.MART TO SHARE sales_share;

-- Allow a target consumer (by account locator or org name + account)
ALTER SHARE sales_share ADD ACCOUNTS = ('ORG1.ACCOUNT_B_ANALYTICS');

Consumer side:

-- In consumer account
CREATE DATABASE SALES_REMOTE FROM SHARE ORG1.PROVIDER_ACCOUNT.sales_share;
-- Now query like a normal DB (read-only)
SELECT * FROM SALES_REMOTE.MART.ORDERS_DAILY;

B) Replicate a database to another region/account

-- On the primary account
ALTER DATABASE SALES ENABLE REPLICATION TO ACCOUNTS = ('ORG1.DR_ACCOUNT');

-- On the DR account: create secondary and refresh on schedule
CREATE DATABASE SALES_DR AS REPLICA OF ORG1.PRIMARY_ACCOUNT.SALES;
-- Later, pull changes (you'll automate this)
ALTER DATABASE SALES_DR REFRESH;

Planned switchover / unplanned failover:

-- In DR account, promote the secondary to primary when needed
ALTER DATABASE SALES_DR PRIMARY;   -- promotion step

-- (Re-point roles/warehouses/connections to the new database name/endpoint)

C) Access control overlays (masking & row access)

-- Example: role-based dynamic masking policy for PII
CREATE MASKING POLICY mask_email AS (val STRING) RETURNS STRING ->
  CASE WHEN current_role() IN ('PII_READ') THEN val
       ELSE REGEXP_REPLACE(val, '(^.).+(@.*$)', '\\1***\\2') END;

ALTER TABLE SALES.CUSTOMERS MODIFY COLUMN EMAIL SET MASKING POLICY mask_email;

-- Row-level filter for region-restricted data
CREATE ROW ACCESS POLICY eu_only AS (region STRING) RETURNS BOOLEAN ->
  region = 'EU' OR current_role() IN ('GLOBAL_AUDIT');

ALTER TABLE SALES.CUSTOMERS ADD ROW ACCESS POLICY eu_only ON (REGION);

RPO/RTO by design

  • Data sharing - RPO ~0 for published objects (consumers see current provider state); RTO ~0 for reads; no write continuity, by design.
  • Replication (no promotion) - RPO is your refresh cadence (e.g., 5-15 minutes); RTO ~0 for read queries on the already-online secondary.
  • Failover (promotion) - RPO is the last successful replication; RTO is minutes, dominated by promotion plus client routing (JDBC/ODBC URLs, secrets, BI connections, dbt profiles). If you haven't rehearsed connection flips and secrets rotation, add 15-60 minutes of chaos to your RTO.

Best practices

  1. Separate blast radii with multi-account. Central ORGADMIN/SECURITYADMIN only; each BU gets ACCOUNTADMIN in its own account without org-wide rights.
  2. Use shares for distribution, replication for resilience. Don't use replication to feed 20 consumers - share once, not copy 20×.
  3. Group what fails over together. Bundle all databases that form one app boundary, including required grants and reference data, and treat it as a unit.
  4. Automate the boring parts. Scheduled REFRESH, pre-created warehouses/roles in DR, and IaC (Terraform Snowflake Provider) for all grants.
  5. Make connection cutover trivial. Use DNS aliases or connection managers so BI/dbt/apps swap endpoints via one variable/secret.
  6. Tighten least privilege before you replicate. If prod roles are messy, DR copies the mess. Enforce RBAC, masking, row policies, network policies, and session parameters now.
  7. Test quarterly. Run a game day: stop replication, promote the secondary, re-run SLA queries, validate lineage, and fail back.
  8. Control cost. Replicate only hot marts; leave raw/bronze as shared or re-creatable. Keep DR warehouses small until an event.

Common pitfalls

  • Relying on shares for DR. Shares are for read distribution, not write continuity.
  • Forgetting future grants. New tables won't be visible if you omit GRANT ... ON FUTURE TABLES.
  • Orphaned policies. Masking/row-access policies must exist in the target; replication handles database objects, but check dependencies.
  • Manual runbooks. Clicking through the UI in an outage is how you miss steps. Script it.
  • Ignoring metadata consumers. Lineage, quality dashboards, and audits need to follow the cutover too.

Worked example

Goal: US primary writes; EU read latency < 100ms; DR in EU with 15-minute RPO.

1. US primary account

  • SALES database → ENABLE REPLICATION to EU_DR_ACCOUNT.
  • Publish sales_share (curated marts) to the EU Analytics account.

2. EU Analytics account

  • CREATE DATABASE SALES_REMOTE FROM SHARE ….sales_share for read-only BI.
  • No local copies, so no ETL drift.

3. EU DR account

  • CREATE DATABASE SALES_DR AS REPLICA OF ….SALES.
  • CRON refresh every 15 minutes; keep small warehouses idled.

4. Runbook

  • If the US region fails: ALTER DATABASE SALES_DR PRIMARY;
  • Flip a single DNS alias / secret used by BI and apps → new Snowflake account URL.
  • Post-incident: perform failback after the US recovers and replication is healthy.

Outcome: EU BI never stopped (reads via share), and write systems resumed in minutes in EU DR with RPO ≤ 15 min.

Access control at scale

  • Org-level: centralize identity via SSO/SCIM; standardize role names across accounts (APP_READ, APP_WRITE, PII_READ).
  • Per account: keep ACCOUNTADMIN for ops with no data access; app roles inherit from functional roles; grant to roles, never to users; apply masking/row policies in the data layers closest to consumers.
  • Secrets & endpoints: store in a central secrets manager; reference by logical alias so failover is configuration, not code changes.

Takeaways

  • Use data sharing for distribution, replication for warm copies, and failover for write continuity.
  • Define RPO/RTO explicitly and back them with automation: refresh schedules, scripted promotion, endpoint flips.
  • Bake in RBAC plus masking/row policies before copying data across regions/accounts.
  • Test quarterly. If you haven't practiced, your RTO is fiction.

Leave a Comment