sql-jdbc-access-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SQL / JDBC Access Design

SQL / JDBC 访问设计

You are a data-platform product designer. Design how a SaaS product exposes its own hosted data to its customers through live SQL - a warehouse share, a read endpoint, or a JDBC/ODBC handle a BI tool connects to - so the surface is isolated, stable, governed, and priced before the first customer connects.
The stakes are asymmetric: an API bug returns a wrong response, but an isolation bug on a live SQL handle returns another customer's rows, and a runaway customer query lands on your infrastructure bill.
The demand side, in a16z's framing: applications are increasingly rebuilt warehouse-native, so governed SQL access is becoming a product category, not a niche feature - analyst positioning for the "why now", not evidence for any implementation choice below.
你是一名数据平台产品设计师。设计一个SaaS产品如何通过实时SQL向客户开放自身托管的数据——一个数仓共享、读取端点,或供BI工具连接的JDBC/ODBC句柄——从而让这个对外界面在第一个客户接入之前就具备隔离性、稳定性、可治理性和定价。
风险是不对称的:API bug返回错误响应,但实时SQL句柄上的隔离bug会返回另一个客户的行;而一个失控的客户查询最终会记在你的基础设施账单上。
需求侧,用a16z的框架来说:应用程序正越来越多地以数仓原生(warehouse-native)的方式重建,因此受治理的SQL访问正在成为一个产品类别,而不是小众功能——这一分析师定位是为了说明“为什么是现在”,不是下面任何实现选择的证据。

Clarifying questions

澄清问题

Ask these before designing anything; each answer changes a later step. Batch them - this is a tactical design task, not a strategy interview.
  1. Which problem is this actually? These get conflated under "data sharing", but only one is this skill's full scope:
    • (a) sharing data with partner organizations - reuses the sharing mechanics here.
    • (b) isolating compute per tenant inside your own product's analytics - an internal architecture concern that this skill's isolation menu touches but pricing and BI sections don't.
    • (c) giving your customers a queryable SQL endpoint to their own data - this skill's full scope.
  2. Where does the data live today: a cloud warehouse (Snowflake, Databricks, BigQuery), an OLTP database, a self-managed OLAP cluster? (decides which architecture rungs are nearly free)
  3. Scan-frequency evidence: will a typical customer run ad-hoc queries a few times a week, or park a dashboard on it that re-scans many times a day? How large are the tables scanned? (this gates the architecture - see step 2)
  4. Tenancy: how many customer accounts, what size spread, and is the store pooled or already siloed per tenant?
  5. Which SQL consumers do customers actually demand: Tableau/Power BI/Looker,
    psql
    and notebooks, or an embedded app or agent issuing queries? (drives the BI-connectivity budget)
  6. Schema exposure: raw operational tables, or a curated mart layer? (raw exposure makes every internal refactor a customer-breaking change - see step 4)
  7. Pricing intent: free feature, gated to a higher tier, or a metered add-on - and who should pay the query compute? (see step 8; also ask the standard re-ranking trio - ship date, one-off vs compounding, effort ceiling)
If your harness has persistent memory, store the design's settled decisions (architecture rung with its promotion condition, isolation mechanism and identity function, schema-stability contract and what counts as breaking, credential ladder position, governance caps, pricing shape) so later runs - a new consumer type, a schema change, a repricing - start from the design instead of re-deriving it.
在设计任何东西之前先问这些问题;每个答案都会改变后续的某个步骤。把它们批量提出——这是一个战术性设计任务,不是战略面试。
  1. 这实际上是哪个问题?这些问题常被混在“数据共享”名下,但只有一个是本skill的完整范围:
    • (a) 与合作伙伴_组织_共享数据——复用这里的共享机制。
    • (b) 在产品自身的分析功能_内部_按租户隔离计算——这是一个内部架构问题,本skill的隔离菜单会涉及,但定价和BI部分不涉及。
    • (c) 让_你的客户_获得一个可查询的SQL端点来访问他们自己的数据——这是本skill的完整范围。
  2. 数据今天在哪里:云数仓(Snowflake、Databricks、BigQuery)、OLTP数据库,还是自托管的OLAP集群?(决定哪些架构档位几乎免费)
  3. 扫描频率证据:一个典型客户是每周几次临时查询,还是把仪表盘停靠在上面、每天多次重新扫描?扫描的表有多大?(这决定架构门槛——见第2步)
  4. 租户:有多少客户账户,规模分布如何,存储是共享池还是已经按租户隔离?
  5. 客户实际需要哪些SQL消费者:Tableau/Power BI/Looker、
    psql
    和notebook,还是嵌入式应用或Agent发出查询?(决定BI连接预算)
  6. Schema暴露:直接暴露原始运营表,还是提供精心整理的建模层?(直接暴露会让每一次内部重构都变成破坏客户的变更——见第4步)
  7. 定价意图:免费功能、放在更高套餐里,还是按量计费的附加组件——以及谁应该支付查询计算成本?(见第8步;还要问标准的重新排序三问——上线日期、一次性与复利性、工作量上限)
如果你的harness有持久记忆,请存储设计已确定的决策(架构档位及其升级条件、隔离机制与身份函数、schema稳定性契约及什么算破坏性变更、凭证阶梯位置、治理上限、定价模式),以便后续运行——新的消费者类型、schema变更、重新定价——从设计出发,而不是重新推导。

Consumer type and scan frequency

消费者类型与扫描频率

Two splits do the work:
  • Consumer type:
    • A BI analyst behind Tableau or Power BI needs a certified driver, stable column names, and predictable types - they never see your docs, only their tool's connection dialog.
    • A data engineer with
      psql
      , dbt, or a notebook tolerates rough edges but pushes the largest queries.
    • An embedded app or agent issues high-frequency machine-generated queries that no human reviews - the strongest case for hard cost caps, since nobody notices a pathological query pattern until the bill.
  • Scan frequency. The same table read twice a week and the same table under a forty-viewer morning dashboard want different architectures (step 2). Frequency, not company size, is the economic variable.
Design for the most demanding consumer type present, and gate the architecture on measured or honestly-estimated scan frequency - never on the org chart of who bought the product.
两个切分起着关键作用:
  • 消费者类型:
    • Tableau或Power BI背后的_BI分析师_需要经过认证的驱动程序、稳定的列名和可预测的类型——他们永远看不到你的文档,只看到他们工具的连接对话框。
    • 使用
      psql
      、dbt或notebook的_数据工程师_能容忍粗糙的边缘,但会跑出最大的查询。
    • _嵌入式应用或Agent_发出高频的机器生成查询,没有人审查——这是强制设置硬性成本上限的最有力理由,因为没有人会注意到病态查询模式,直到账单出现。
  • 扫描频率。 同一张表每周读两次,和同一张表被一个早上四十人看的仪表盘反复扫描,需要不同的架构(第2步)。决定经济性的变量是频率,不是公司规模。
为当前存在的最高要求的消费者类型设计,并根据实测或诚实估计的扫描频率来设置架构门槛——绝不要根据谁购买了产品的组织架构。

Workflow

工作流程

  1. Confirm the problem and inventory the consumers.
  2. Gate the architecture on scan economics.
  3. Enforce tenant isolation in the engine.
  4. Publish a schema-stability contract.
  5. Design BI connectivity and driver distribution.
  6. Configure query governance and cost caps.
  7. Issue credentials.
  8. Choose the pricing shape.
Each step has a section below, in order. Steps 3 and 6 must both pass their gates before any customer gets a handle; everything else can stage.
  1. 确认问题并盘点消费者。
  2. 用扫描经济性决定架构。
  3. 在引擎中强制租户隔离。
  4. 发布schema稳定性契约。
  5. 设计BI连接与驱动分发。
  6. 配置查询治理与成本上限。
  7. 颁发凭证。
  8. 选择定价模式。
每一步都有下面的对应章节,按顺序进行。第3步和第6步必须在任何客户获得句柄之前都通过各自的门槛;其余可以分阶段。

1. Confirm the problem and inventory the consumers

1. 确认问题并盘点消费者

  • Settle question 1 explicitly and write the answer down. "Data sharing", "multi-tenant isolation", and "customer SQL access" name overlapping but distinct architectures, and a design that answers the wrong one ships the wrong isolation primitive.
  • List the concrete consumers (question 5) with their expected query shape: dashboard refresh, ad-hoc exploration, scheduled extract, agent traffic. This list is the input to every ranked menu below.
  • State the boundary for the reader: this surface serves live queries. If the consumer actually wants periodic files or their own warehouse fed, hand off to the bulk-delivery sibling skill (see References) before designing anything here.
  • 明确解决第1个问题并把答案写下来。“数据共享”、“多租户隔离”和“客户SQL访问”描述的是重叠但不同的架构,回答了错误问题的设计会交付错误的隔离原语。
  • 列出具体消费者(问题5)及其预期查询形态:仪表盘刷新、临时探索、定时抽取、Agent流量。这个列表是下面每个排序菜单的输入。
  • 为读者明确边界:这个界面服务于_实时查询_。如果消费者实际上想要定期文件或喂养他们自己的数仓,在设计这里任何东西之前,交给批量交付兄弟skill(见参考文献)。

2. Gate the architecture on scan economics

2. 用扫描经济性决定架构

Three architectures, ranked. Scan frequency and where the data already lives decide which one wins - not taste.
  • efficiency:
    zero-copy share > replicated copy or read endpoint > per-tenant compute isolation
  • effort:
    per-tenant compute isolation > replicated copy or read endpoint > zero-copy share
  • value:
    per-tenant compute isolation > zero-copy share > replicated copy or read endpoint
  • Default rung: zero-copy warehouse sharing - when the data already lives in a shared cloud warehouse and customers scan infrequently. The share is a metadata grant, not a copy: lowest build cost, always-fresh data, governance stays centralized at the source. The cost model is the catch: live access is paid per query scan (plus cross-region egress), so the economics hold only while scans stay infrequent.
  • Step to a replicated copy or dedicated read endpoint when scan frequency inverts the arithmetic.
    • One lakehouse practitioner's heuristic (Alex Merced - treat it as one practitioner's rule of thumb, not a benchmark): a copy is paid once per refresh, live access once per query.
    • At roughly 20 scans/day on a ~200 GB table, the egress of live re-scanning costs more per month than a replicated copy costs per year.
    • A dashboard forty people load every morning is past this line. Two analysts querying twice a day are not.
    • The price of this rung: staleness to the last refresh, and a second copy of the data (including its PII) to govern.
    • Never point the replica at the live operational schema directly - see step 4.
  • Promote to per-tenant compute isolation - each tenant gets its own query engine (a per-tenant instance over shared storage, fronted by one endpoint). This is the starved option:
    • Highest value: noisy neighbors eliminated by construction, a runaway query's blast radius capped at one tenant, clean per-tenant cost attribution.
    • Highest effort: an engine fleet to build and operate, so it loses every efficiency round.
    • Two conditions promote it anyway: arbitrary customer-authored SQL at dashboard-or-agent frequency across many tenants, or a shared cluster whose worst-case query already threatens the product itself.
    • PostHog hit both, judged unbounded customer queries against its shared cluster "basically untenable", and rebuilt on per-organization engines behind a Postgres wire-protocol endpoint (see references/vendor-case-studies.md).
This ranking is a default, not a law. Re-rank against the interview:
  • Already on Snowflake/Databricks/BigQuery makes zero-copy nearly free (which is why it's the default at all).
  • An OLTP-only stack with no warehouse deletes the zero-copy rung entirely rather than demoting it.
  • A hard ship date promotes whichever rung your platform gives you today.
  • Agent consumers or a compounding-asset mandate promote the isolation rung despite its effort.
三种架构,按排序。扫描频率和数据已存在的位置决定哪个胜出——不是品味。
  • 效率:
    零拷贝共享 > 复制副本或读取端点 > 按租户计算隔离
  • 工作量:
    按租户计算隔离 > 复制副本或读取端点 > 零拷贝共享
  • 价值:
    按租户计算隔离 > 零拷贝共享 > 复制副本或读取端点
  • 默认档位:零拷贝数仓共享——当数据已经存在于共享云数仓且客户扫描不频繁时。共享是一种元数据授权,不是副本:构建成本最低、数据始终新鲜、治理集中在源头。成本模型是问题所在:实时访问按每次查询扫描付费(加上跨区域出口),所以只有扫描保持不频繁时经济性才成立。
  • 当扫描频率反转算术时,升级到复制副本或专用读取端点。
    • 一位lakehouse从业者的启发式(Alex Merced——把它当作单个从业者的经验法则,不是基准):副本每次刷新付一次费,实时访问每次查询付一次费。
    • 在约200 GB的表上大约每天20次扫描时,实时重新扫描的出口成本每月高于复制副本每年的成本。
    • 一个四十人每天早上加载的仪表盘已经过了这条线。两个分析师每天查询两次则没有。
    • 这一档位的代价:数据陈旧到上次刷新,以及需要治理的第二个数据副本(包括其中的PII)。
    • 绝不要把副本直接指向实时运营schema——见第4步。
  • 升级到按租户计算隔离——每个租户获得自己的查询引擎(共享存储之上的每租户实例,由一个端点统一入口)。这是一个被饿死的选项:
    • 最高价值:通过构造消除噪声邻居,失控查询的爆炸半径限制在一个租户,干净地按租户归因成本。
    • 最高工作量:需要构建和运维一个引擎集群,所以它在每一轮效率比较中都输。
    • 两个条件仍然会促使其升级:跨多个租户在仪表盘或Agent频率上运行任意客户编写的SQL,或者一个共享集群的最坏情况查询已经威胁到产品本身。
    • PostHog两个条件都命中,判定无限客户查询对共享集群“基本上不可行”,并在Postgres wire-protocol端点后面基于每组织引擎重建(见 references/vendor-case-studies.md)。
这个排序是默认值,不是定律。根据访谈重新排序:
  • 已经在Snowflake/Databricks/BigQuery上使零拷贝几乎免费(这也是它成为默认值的原因)。
  • 只有OLTP、没有数仓的栈会完全删除零拷贝档位,而不是降级。
  • 硬性上线日期会推向你平台今天能提供的那个档位。
  • Agent消费者或复利资产的任务会推动隔离档位,尽管工作量更高。

3. Enforce tenant isolation in the engine

3. 在引擎中强制租户隔离

Isolation must live in the query engine, never in application code. An app-layer
WHERE tenant_id = ?
filter is a named failure class for this surface: with an external SQL handle there is no application layer left to intercept the query, so one missed filter - ever, anywhere - leaks another customer's rows. Engine-level policies make a query that omits the filter return nothing instead.
  • On a warehouse share, expose only secure/dynamic views that join a private base table to a private entitlement table filtered on the caller's identity as the engine sees it - Snowflake's
    current_account()
    , Databricks Delta Sharing's
    current_recipient()
    . The base and mapping tables never enter the share.
  • The NULL-context trap:
    CURRENT_ROLE()
    and
    CURRENT_USER()
    return
    NULL
    inside a secure view once it crosses an account-to-account share. A policy written against them silently matches nothing - or worse, everything, depending on the predicate's shape. Only the share-aware identity functions are safe across a share boundary.
  • Treat recipient-property partition filtering as a performance optimization, never as access control - Delta Sharing documents it as best-effort. Hard isolation comes from the dynamic-view predicate alone.
  • On Postgres-family engines, use row-level security with
    FORCE ROW LEVEL SECURITY
    , and inject tenant identity into the session context server-side from a verified token - never from client-supplied input. RLS has four well-documented failure modes (policy recursion, missing
    WITH CHECK
    , permissive-policy OR-composition, owner-privileged view bypass) plus performance rules; work through references/rls-failure-mode-catalog.md before writing any policy.
  • Verify isolation from the consumer's side, not the provider's: run probe queries as another tenant (a second test account, or the warehouse's simulated-consumer session mode) and require zero foreign rows. This is a gate (see Measurement), re-run on every policy change.
隔离必须存在于查询引擎中,绝不能在应用程序代码中。应用层的
WHERE tenant_id = ?
过滤器对这个界面来说是一个命名的失败类别:对于外部SQL句柄,已经没有任何应用层可以拦截查询,所以一个漏掉的过滤器——无论何时何地——就会泄露另一个客户的行。引擎级策略让省略过滤器的查询反而返回空结果。
  • 在数仓共享上,只暴露安全/动态视图,这些视图将一个私有基础表联接到一个私有权限表,并基于_引擎看到的调用者身份_过滤——Snowflake的
    current_account()
    、Databricks Delta Sharing的
    current_recipient()
    。基础表和映射表绝不进入共享。
  • NULL上下文陷阱:一旦穿过账户到账户的共享,
    CURRENT_ROLE()
    和
    CURRENT_USER()
    在安全视图内部返回
    NULL
    。针对它们编写的策略会静默地匹配不到任何东西——或者更糟,根据谓词的形状匹配所有东西。只有能感知共享的身份函数在共享边界上才是安全的。
  • 将接收方属性的_分区过滤_当作性能优化,绝不当作访问控制——Delta Sharing将其文档化为best-effort。硬隔离只来自动态视图谓词。
  • 在Postgres家族引擎上,使用带
    FORCE ROW LEVEL SECURITY
    的行级安全,并从验证过的token在服务端将租户身份注入会话上下文——绝不来自客户端提供的输入。RLS有四个记录在案的失败模式(策略递归、缺失
    WITH CHECK
    、permissive策略的OR组合、owner特权视图绕过)加上性能规则;在写任何策略之前,先过一遍 references/rls-failure-mode-catalog.md。
  • 从消费者一侧而不是提供者一侧验证隔离:作为另一个租户运行探测查询(第二个测试账户,或数仓的模拟消费者会话模式),要求返回零外部行。这是一个门槛(见衡量),每次策略变更时重新运行。

4. Publish a schema-stability contract

4. 发布schema稳定性契约

The moment a customer's dashboard queries your tables, your schema is an API: columns are fields, tables are endpoints, their dashboards are clients. The asymmetry versus API versioning is that schema changes often happen accidentally - a source-system update, a readability rename - and propagate downstream with no deprecation header to warn anyone.
  • Never expose the live operational schema. Serve a curated mart layer replicated from the system of record, so internal schema churn stops at the replication boundary instead of landing in customer dashboards.
  • Adopt the additive-only rule: add columns and tables freely; never rename, retype, or drop in place. Evolve via expand/contract:
    1. Add the new column.
    2. Deprecate the old one in the docs.
    3. Migrate consumers.
    4. Remove only when nothing depends on it.
    Amplitude's event schema is a shipped existence proof: it only ever adds tables and columns as new event types arrive, staying backward-compatible by construction.
  • Enforce the contract in CI, not by convention. A contract that only lives in a doc is a hope:
    • dbt model contracts (1.5+) fail the run on a breaking column change.
    • The open-source
      datacontract
      CLI validates a contract against a PR before merge.
  • Write the contract in four parts, because a BI dashboard, a dbt job, and an embedded app each depend on a different subset:
    • Schema: columns, types, nullability.
    • Quality: completeness thresholds.
    • Freshness: refresh SLA.
    • Availability: uptime/latency.
  • When a breaking change is genuinely unavoidable, it exits through deprecation machinery with notice periods - the versioning sibling skill governs that; this contract defines what counts as breaking.
当客户的仪表盘开始查询你的表时,你的schema就是一个API:列是字段,表是端点,他们的仪表盘是客户端。与API版本化的不对称性在于,schema变更常常是_意外_发生的——一个源系统更新、一个可读性重命名——并且在没有弃用头警告任何人的情况下向下游传播。
  • 绝不暴露实时运营schema。提供从系统记录复制的精心整理建模层,这样内部schema变动停在复制边界,而不是落在客户仪表盘中。
  • 采用仅增规则:自由添加列和表;绝不原地重命名、改类型或删除。通过expand/contract演进:
    1. 添加新列。
    2. 在文档中弃用旧列。
    3. 迁移消费者。
    4. 只有当没有东西依赖它时才删除。
    Amplitude的事件schema是一个已上线的存在证明:它只会随着新事件类型的到来而添加表和列,通过构造保持向后兼容。
  • 在CI中执行契约,而不是靠约定。只存在于文档中的契约是一种希望:
    • dbt模型契约(1.5+)在破坏性列变更时使运行失败。
    • 开源
      datacontract
      CLI 在合并前针对PR验证契约。
  • 用四个部分写契约,因为BI仪表盘、dbt作业和嵌入式应用各自依赖不同子集:
    • Schema:列、类型、可空性。
    • 质量:完整性阈值。
    • 新鲜度:刷新SLA。
    • 可用性:可用性/延迟。
  • 当破坏性变更确实不可避免时,它通过带通知期的弃用机制退出——版本控制兄弟skill负责治理;本契约定义了什么算破坏性。

5. Design BI connectivity and driver distribution

5. 设计BI连接与驱动分发

BI-tool support is a certification tax, not a one-time integration - budget it as ongoing engineering, and let question 5 decide how much of it you buy.
  • The cheapest rung, when your engine allows it: speak an existing wire protocol. A Postgres wire-protocol endpoint gets every BI tool,
    psql
    , and every ORM for free, because they all ship a Postgres driver - this is exactly why PostHog fronted its per-tenant engines with one. It costs you dialect fidelity: you support the Postgres dialect and nothing exotic.
  • A custom dialect is the anti-pattern this rung exists to avoid: PostHog names its own HogQL as a reason direct exposure was a non-starter - "not a language that's already widely supported" means every BI tool integration starts from zero.
  • Shipping your own driver means:
    • A spec-compliant Type-4 JDBC driver with accurate SQLSTATEs (Tableau warns that a generic error instead of SQLSTATE 28000 for bad credentials breaks connector behavior).
    • Signed per-tool connector packages with certificates that expire and must be renewed.
    • Per-tool certification programs whose policies route first-line connectivity support back to you, not to the BI vendor.
    Details and the certification requirements per tool: references/bi-connectivity-and-credentials.md.
  • Publish a connection-setup page per supported client. An undocumented connection string is a support ticket per customer:
    • URL template.
    • Driver class or download.
    • TLS requirement stated explicitly rather than left to client defaults.
    • Auth method.
BI工具支持是一种认证税,不是一次性集成——按持续工程预算,并让问题5决定你买多少。
  • 最便宜的档位,当你的引擎允许时:说一种现有的wire protocol。一个Postgres wire-protocol端点可以免费获得所有BI工具、
    psql
    和每个ORM,因为它们都自带Postgres驱动——这正是PostHog用它作为每租户引擎前端的原因。代价是方言保真度:你支持Postgres方言,不支持任何奇异的方言。
  • 自定义方言正是这一档位要避免的反模式:PostHog称自己的HogQL是直接暴露无法启动的原因——“不是一种已被广泛支持的语言”意味着每个BI工具集成都从零开始。
  • 发布自己的驱动意味着:
    • 一个符合规范的Type-4 JDBC驱动,具有_准确_的SQLSTATE(Tableau警告说,对于错误凭据,通用错误而不是SQLSTATE 28000会破坏连接器行为)。
    • 带证书的每个工具连接器包,证书会过期并需要续签。
    • 每个工具的认证计划,其策略将第一线连接支持路由回你,而不是BI厂商。
    详细信息和每个工具的认证要求:references/bi-connectivity-and-credentials.md。
  • 为每个支持的客户端发布连接设置页面。一个没有文档的连接字符串就是每个客户一张支持工单:
    • URL模板。
    • 驱动类或下载。
    • 明确说明TLS要求,而不是留给客户端默认值。
    • 认证方法。

6. Configure query governance and cost caps

6. 配置查询治理与成本上限

None of the warehouse cost controls are on by default; every one must be configured before GA, per external warehouse or engine. This differs from API rate limiting in enforcement primitive: you are capping compute seconds and spend on a long-running engine, not counting requests at a gateway. Design it here, and keep request-style limits (connection counts, queries/minute at the endpoint) consistent with the API sibling skill's policy.
  • Per-query circuit breaker: a statement timeout so one runaway query dies instead of running all night, plus a queued-statement timeout so queries don't wait indefinitely for a slot.
  • Concurrency cap per tenant or workload, so one customer's parallel dashboard refresh can't occupy a shared engine.
  • A spend cap distinct from the timeout: a resource monitor (or your engine's equivalent budget alarm) that suspends the warehouse when its credit budget is hit. The timeout bounds one query; the monitor bounds the month.
  • The reader-account trap: when you provision consumer accounts whose compute bills to you (Snowflake reader accounts, or any provider-pays posture from step 8), their warehouses can consume unlimited credits charged to your account by default. A resource monitor on every such account is the single most important cost-control step of this surface, not optional hardening.
  • The escalation signal: a tenant that regularly queues others or trips monitors moves to isolated or larger compute; never raise the shared cap for everyone to accommodate one tenant.
数仓成本控制没有一个是默认开启的;每一个都必须在GA之前为每个外部数仓或引擎配置。这与API限流在执行原语上不同:你是在一个长期运行的引擎上限制计算秒数和花费,而不是在网关处计数请求。在这里设计它,并让请求式限制(连接数、端点处每分钟查询数)与API兄弟skill的策略保持一致。
  • 每次查询的断路器:语句超时,让一个失控查询死掉而不是跑一整夜,加上排队语句超时,让查询不会无限期等待一个slot。
  • 每个租户或工作负载的并发上限,这样一个个客户的并行仪表盘刷新不能占满共享引擎。
  • 一个与超时不同的花费上限:一个资源监视器(或你引擎等效的预算警报),在其配额预算耗尽时暂停数仓。超时限制一个查询;监视器限制一个月。
  • reader-account陷阱:当你配置消费者账户、其计算账单由_你_承担时(Snowflake reader accounts,或第8步中任何provider-pays姿态),默认情况下它们可以无限消耗记在你账户上的信用额度。在每个这样的账户上设置资源监视器是这个界面最重要的成本控制步骤,不是可选的加固。
  • 升级信号:一个经常让其他查询排队或触发监视器的租户,应迁移到隔离或更大的计算;绝不要为了迁就一个租户而提高全员共享上限。

7. Issue credentials

7. 颁发凭证

The industry is retiring static database passwords - Snowflake enforces this through a staged deprecation schedule that moves all service users onto key-pair, OAuth, PAT, or workload identity - so design credential issuance on that trajectory rather than grandfathering passwords in.
Preference order: short-lived token > managed secret > static password; inline credentials in a connection string are the anti-pattern no documented path should show.
  • Service accounts and automation: key-pair auth signing short-lived JWTs, so no password ever crosses the network; mark machine identities as service-type to close the password fallback.
  • BI tools: OAuth (authorization-code with PKCE for desktop tools) so the BI tool never stores a database password. Caveat: token-refresh support varies by client - a BI tool that can't hold a refreshable token degrades to a key-pair or PAT service account, so the ordering must degrade gracefully rather than assume OAuth everywhere.
  • Scope every credential to the tenant's own data by construction (the entitlement mapping from step 3), enforce TLS as a stated requirement, and give customers self-service revocation. Key formats, rotation UX, and secret hygiene follow the credential sibling skill; specifics for this surface live in references/bi-connectivity-and-credentials.md.
行业正在淘汰静态数据库密码——Snowflake通过一个分阶段的弃用时间表强制执行这一点,将所有服务用户迁移到key-pair、OAuth、PAT或workload identity——所以设计凭证颁发要沿这个轨迹,而不是把密码grandfathered进来。
偏好顺序:短期token > 托管secret > 静态密码;连接字符串中的内联凭据是反模式,任何有文档的路径都不应展示。
  • 服务账户和自动化:key-pair认证签署短期JWT,所以没有密码穿过网络;将机器身份标记为service类型,关闭密码回退。
  • BI工具:OAuth(桌面工具用authorization-code with PKCE),使BI工具永不存储数据库密码。注意:token刷新支持因客户端而异——一个不能持有可刷新token的BI工具会退化为key-pair或PAT服务账户,所以排序必须优雅降级,而不是假设到处都是OAuth。
  • 通过构造将每个凭据限定在租户自己的数据上(第3步的权限映射),将TLS作为明确要求执行,并给客户自助撤销。密钥格式、轮换UX和secret卫生遵循凭证兄弟skill;本界面的具体信息在 references/bi-connectivity-and-credentials.md。

8. Choose the pricing shape

8. 选择定价模式

Package SQL access as a paid tier or add-on, never a free feature. The shipped precedents (Stripe Sigma, Heap Connect, Mixpanel Data Pipelines) show customers pay for exactly this capability, and a price is itself a governance control on a surface that is expensive to over-serve. Three shapes, ranked:
  • efficiency:
    flat add-on or tier gate > consumption / volume-tiered > provider-pays-compute passthrough
  • effort:
    consumption / volume-tiered > provider-pays-compute passthrough > flat add-on or tier gate
  • value:
    consumption / volume-tiered > provider-pays-compute passthrough > flat add-on or tier gate
  • Default rung: the flat add-on or tier gate (Heap Connect, Mixpanel's shape) - SQL access unlocks at a higher plan or as a fixed-price add-on. Near-zero billing engineering; the gate itself throttles casual abuse. Its risk is the unbounded heavy user whose compute costs exceed the flat price - acceptable only with step 6's caps in place.
  • Promote to consumption / volume-tiered when your own cost exposure is consumption-based or customer data volumes span orders of magnitude. This is the starved option:
    • Highest value: revenue tracks cost, so no customer is quietly unprofitable.
    • Highest effort: metering, tier tables, overage logic, so it loses the efficiency round until one of those two conditions holds.
    • Meter on a business unit the buyer already understands (Stripe Sigma tiers on monthly charge volume with unmetered queries), never on warehouse credits a non-technical buyer can't predict.
    • Sigma also shows the metric is revisable: it changed its own pricing model after shipping.
  • Provider-pays-compute passthrough is less a free choice than the posture that arrives with reader accounts and marketplace listings: you pay the compute, then invoice cost plus margin. Take it only with a resource monitor on every account (step 6) - and note the consumer-pays alternative exists at the same rung: HubSpot's Snowflake share bills compute to the customer's own account, gated to its top tier.
This ranking is a default, not a law - re-rank against question 7 and the interview trio:
  • A compounding-asset mandate promotes the consumption rung early, since retrofitting a meter under a shipped flat price is a customer-facing repricing.
  • A hard ship date holds the flat gate, which is the only rung that needs no billing engineering.
Pricing precedents with their point-in-time figures: references/vendor-case-studies.md - verify current pricing before quoting any of them to a customer.
将SQL访问打包为付费档位或附加组件,绝不是免费功能。已上线的先例(Stripe Sigma、Heap Connect、Mixpanel Data Pipelines)表明客户正是为这种能力付费,而价格本身就是一个治理控制,用于这个过度服务代价高昂的界面。三种形态,排序:
  • 效率:
    固定附加组件或档位门槛 > 消费/按量分级 > provider-pays计算转嫁
  • 工作量:
    消费/按量分级 > provider-pays计算转嫁 > 固定附加组件或档位门槛
  • 价值:
    消费/按量分级 > provider-pays计算转嫁 > 固定附加组件或档位门槛
  • 默认档位:固定附加组件或档位门槛(Heap Connect、Mixpanel的形态)——SQL访问在更高套餐解锁或以固定价格附加组件提供。计费工程接近零;门槛本身就抑制了随意滥用。风险是无上限的重度用户,其计算成本超过固定价格——只在第6步的上限到位时可接受。
  • 当你的成本敞口是基于消费的,或客户数据量跨数量级时,升级到消费/按量分级。 这是被饿死的选项:
    • 最高价值:收入跟随成本,所以没有客户悄悄不盈利。
    • 最高工作量:计量、分级表、超量逻辑,所以它在效率上输,直到这两个条件之一成立。
    • 按买家已经理解的业务单位计量(Stripe Sigma按月度收费量分级,查询不计量),绝不要按非技术买家无法预测的数仓信用额度计量。
    • Sigma也表明指标是可修订的:它在发布后改变了自己的定价模型。
  • Provider-pays计算转嫁与其说是一个自由选择,不如说是随reader accounts和市场列表而来的姿态:你先付计算费,然后按成本加价开票。只有在每个账户都有资源监视器(第6步)的情况下才采用——注意同一档位还有consumer-pays替代方案:HubSpot的Snowflake共享将计算账单记到客户自己的账户,并限制在其最高档位。
这个排序是默认值,不是定律——根据问题7和访谈三问重新排序:
  • 复利资产任务会提早推动消费档位,因为在一个已上线的固定价格下改造计量表是一次面向客户的重新定价。
  • 硬性上线日期保持固定门槛,这是唯一不需要计费工程的档位。
带时点数字的定价先例:references/vendor-case-studies.md——在向客户引用其中任何一个之前,核实当前定价。

Failure modes

失败模式

Anti-pattern checklist - each is a direct audit finding:
  • Tenant isolation implemented as an application-layer
    WHERE
    clause instead of engine policy.
  • A shared-view policy written against
    CURRENT_ROLE()
    /
    CURRENT_USER()
    (NULL across shares) instead of the share-aware identity functions.
  • Partition filtering trusted as access control.
  • Customer SQL admitted onto the shared production cluster - the PostHog "untenable" scenario.
  • The live operational schema exposed directly, so internal refactors break customer dashboards.
  • A rename or retype shipped in place instead of expand/contract, with no CI contract to catch it.
  • A reader account (or any provider-pays compute) with no resource monitor.
  • A custom query dialect where a standard wire protocol was available.
  • Schema-per-tenant chosen for isolation, collapsing under connection pooling and DDL migrations past a few hundred tenants.
  • Isolation verified only from the provider's side, never probed as a consumer.
  • RLS-specific traps (recursion, missing
    WITH CHECK
    , OR-composition, view bypass): the full catalog with fixes is in references/rls-failure-mode-catalog.md.
反模式清单——每一项都是一条直接的审计发现:
  • 租户隔离实现为应用层
    WHERE
    子句,而不是引擎策略。
  • 共享视图策略针对
    CURRENT_ROLE()
    /
    CURRENT_USER()
    编写(跨共享为NULL),而不是感知共享的身份函数。
  • 将分区过滤信任为访问控制。
  • 客户SQL被允许进入共享生产集群——PostHog“不可行”的场景。
  • 直接暴露实时运营schema,使内部重构破坏客户仪表盘。
  • 原地重命名或改类型,而不是expand/contract,且没有CI契约捕获。
  • reader account(或任何provider-pays计算)没有资源监视器。
  • 在有标准wire protocol可用时使用自定义查询方言。
  • 为了隔离选择schema-per-tenant,在超过几百个租户后因连接池和DDL迁移而崩溃。
  • 只从提供者一侧验证隔离,从不作为消费者探测。
  • RLS特定陷阱(递归、缺失
    WITH CHECK
    、OR组合、视图绕过):完整目录及修复见 references/rls-failure-mode-catalog.md。

Measurement

衡量

  • Isolation gate: probe queries executed as another tenant (simulated-consumer session or a second test account) return zero foreign rows, across every shared view and policy - binary, before GA and after every policy or schema change.
  • Schema gate: zero breaking changes reach the customer-facing schema past the CI contract check; a breaking change that ships is a gate failure even if no customer noticed.
  • Cost gate: every external compute handle has a statement timeout, a concurrency cap, and a spend cap before the first customer connects - audited as a checklist, pass/fail per handle.
  • Trends to baseline from the first month, not thresholds: connectivity support tickets per new connection, per-tenant query cost against price paid, freshness-SLA adherence. A customer-facing data surface carries a categorically higher reliability bar than an internal dashboard - a failure here is a support ticket and a churn risk, so trend regressions get triaged like product incidents.
Iterate until all three gates pass; they are gates, not aspirations. The trends steer pricing and architecture revisits afterwards.
  • 隔离门槛:作为另一个租户执行的探测查询(模拟消费者会话或第二个测试账户)在跨越每个共享视图和策略时返回零外部行——二元的,在GA之前和每次策略或schema变更之后。
  • Schema门槛:通过CI契约检查后,零破坏性变更到达客户可见schema;一个上线的破坏性变更就是门槛失败,即使没有客户注意到。
  • 成本门槛:在每个外部计算句柄上,第一个客户连接之前都有语句超时、并发上限和花费上限——作为清单审计,每个句柄通过/失败。
  • 从第一个月开始建立基线趋势,而不是阈值:每个新连接的连接支持工单、每个租户查询成本与支付价格、新鲜度SLA遵守情况。面向客户的数据表面承载着比内部仪表盘更高的可靠性标准——这里的失败是一张支持工单和一个流失风险,所以趋势回归要像产品事故一样分类处理。
迭代直到所有三个门槛通过;它们是门槛,不是愿望。之后趋势指导定价和架构复审。

Invocation examples

调用示例

  • "Our customers keep asking to plug Tableau into their data in our product - design the SQL access surface, we're on Snowflake."
  • "We want to sell direct SQL access as an Enterprise add-on. What architecture and pricing shape, given most customers would park dashboards on it?"
  • "Review our plan to expose read replicas of our production Postgres to customers - what breaks?"
  • “我们的客户一直要求把Tableau插到他们在我们产品中的数据上——设计SQL访问界面,我们在Snowflake上。”
  • “我们想把直接SQL访问作为Enterprise附加组件出售。考虑到大多数客户会把仪表盘停靠在其上,应该用什么架构和定价形态?”
  • “审查我们向客户暴露生产Postgres只读副本的计划——会破坏什么?”

References

参考文献

  • samber/developer-platform-skills@etl-connector-strategy
    - being a source connector on other people's ETL platforms; this skill is the customer querying you directly.
  • samber/developer-platform-skills@api-integration-surface-strategy
    - the umbrella decision of whether SQL access should be offered at all, among the other integration surfaces.
  • samber/developer-platform-skills@api-auth-key-management
    - key formats, rotation UX, and secret hygiene for API credentials; step 7 applies the same discipline to database handles.
  • samber/developer-platform-skills@api-versioning-policy
    - deprecation machinery and notice periods; step 4's contract defines what counts as breaking for the schema, that skill governs how a breaking change is announced and retired.
  • samber/developer-platform-skills@api-rate-limit-policy
    - request-counting limits at the API gateway; step 6 caps compute and spend on a query engine instead, and the two published policies must not contradict each other.
  • samber/developer-platform-skills@etl-connector-strategy
    ——作为别人ETL平台上的源连接器;本skill是客户直接查询你。
  • samber/developer-platform-skills@api-integration-surface-strategy
    ——是否应提供SQL访问这个总括决策,以及其它集成界面。
  • samber/developer-platform-skills@api-auth-key-management
    ——API凭据的密钥格式、轮换UX和secret卫生;第7步将同样的纪律应用于数据库句柄。
  • samber/developer-platform-skills@api-versioning-policy
    ——弃用机制和通知期;第4步契约定义了什么对schema算破坏性,那个skill治理破坏性变更如何宣布和退役。
  • samber/developer-platform-skills@api-rate-limit-policy
    ——API网关处的请求计数限制;第6步改为在查询引擎上限制计算和花费,两个已发布策略不得互相矛盾。