Multi-Tenant Data Sharing With Row-Level Isolation and Cross-Region Consumers
DataCo publishes a curated PRODUCT_USAGE database to 14 consumer accounts. Nine consumers are in the same AWS region; three are in eu-central-1 while DataCo's primary account is in us-east-1; two are on Azure. Consumer A's contract requires that its analysts see only rows for its own TENANT_ID, and DataCo must be able to prove the filter cannot be removed by the consumer. Consumer B reports that queries against the shared database fail with 'does not exist or is not authorized' even though the database appears in its account. Consumer C wants to build its own views over the data and grant them to its internal teams. Meanwhile, replication and storage line items in DataCo's bill have grown faster than the number of consumers.
1. Design the tenant isolation for Consumer A so that it is structurally enforceable, not just conventional.
Read the worked answer
Put the filter where the consumer has no write access, and make sure every access path inherits it.
- Never grant on the base table. The share should contain only a view. Grant USAGE on the database and schema plus SELECT on the view.
- Use a secure view. A non-secure view exposes its definition to the consumer, which leaks schema and logic; secure views also prevent consumers from using the definition to infer row counts or reconstruct filtered values. Because the secure view references objects, the provider must grant REFERENCE_USAGE on the referenced database to the share.
- Resolve the tenant without hard-coding it. Either a mapping table (
account_locator -> tenant_id) joined onCURRENT_ACCOUNT(), or a row access policy attached to the base table whose body resolves the caller's account to a tenant. The policy-on-base-table version is stronger: any future view, clone, or direct grant still gets filtered. - Beware the silent failure mode. If the mapping lookup returns no row, the policy filters everything and the consumer sees zero rows - safe but confusing. Return NULL deliberately and document it, and monitor for empty-result reports after onboarding.
- One share with a mapping scales better than 14 hand-built shares, but remember every new consumer is a new entry in the mapping table - make that a controlled, reviewed step.
Finally, test as the consumer: create a test account, accept the share, and confirm SHOW VIEWS reveals no definition and that a SELECT * returns only the mapped tenant.
2. Consumer B gets 'does not exist or is not authorized'. Walk through the privilege chain and identify the most likely causes.
Read the worked answer
That error almost always means the object is visible in metadata but the role lacks a privilege somewhere in the chain. Check in this order:
- Consumer side - IMPORTED PRIVILEGES. The role used must have IMPORTED PRIVILEGES on the database created from the share. This is the single most common miss, and it is granted by the consumer, not the provider.
- Provider side - the full grant chain inside the share. USAGE on the database, USAGE on each schema, SELECT on each table or view. A grant on the table alone is not enough.
- Provider side - REFERENCE_USAGE. If a shared secure view references tables in another database or schema, the share needs REFERENCE_USAGE on those containers. Without it the view exists but cannot be resolved at query time, which produces exactly this error.
- Objects added after the share was created. If the table was added to the share later, confirm the provider actually re-ran the grant and that the consumer's database has imported privileges on the container that now holds it.
- Region/cloud mismatch. A share does not cross regions or clouds. If Consumer B is in eu-central-1 or on Azure and DataCo has not replicated and created a listing there, the database may appear stale or partially populated.
- Wrong database. Consumers sometimes query a locally created database with the same name rather than the imported one. Confirm with
SHOW DATABASESand theorigincolumn.
Ask B for the exact query and the role it ran under; the object named in the error tells you which link in the chain broke.
3. How would you serve the cross-region and Azure consumers, and what can Consumer C legitimately do with its own views?
Read the worked answer
Cross-region / cross-cloud. A share is regional, so the options are:
- Replicate the database into the target region and create the share or listing from the secondary. Queries then run entirely in-region with no egress per query, but you pay storage for the replica plus refresh compute on every replication cycle. Refresh cadence is the cost dial - hourly is far cheaper than continuous, so negotiate freshness against the contract.
- Cross-Cloud Auto-Fulfillment / a Marketplace listing if DataCo wants Snowflake to manage the regional copies for it, at the cost of less control over copy placement.
- Reader accounts when the consumer will not create its own Snowflake account. Reader accounts live in the provider's region, so they solve governance and billing, not locality - an eu consumer on a us-east-1 reader account still pays the latency.
Given nine consumers already sit in-region and only three plus two are remote, replicate once per remote region rather than per consumer.
Consumer C. C can create views in its own account over the shared objects and grant those views to its own roles - that is normal local governance and it does not affect DataCo. What C cannot do is include DataCo's objects in an outbound share of its own: re-sharing shared data is not permitted, because DataCo remains the data owner. If C needs to redistribute, DataCo must add C's downstream parties as consumers of the same share (or a narrower one), or use a clean-room arrangement. Say this explicitly up front - it is a frequent architectural surprise mid-project.