ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PostHog Data Warehouse 导入源新增/弃用供应商 API 版本的完整指南

2026/9/10 12:32:41 拓冰建站 浏览量
PostHog Data Warehouse 导入源新增/弃用供应商 API 版本的完整指南 PostHog Data Warehouse 导入源新增/弃用供应商 API 版本的完整指南【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog本篇技术指南以 PostHog 仓库中的官方 Skill 文档.agents/skills/warehouse-source-new-version/SKILL.md为核心骨架系统讲解 PostHog Data Warehouse 导入源位于products/warehouse_sources/backend/temporal/data_imports/sources/如何为既有供应商新增 API 版本支持、如何弃用旧版本以及 pinning版本固定语义与常见陷阱。读者学完后将掌握从判断新版本是否需要支持到声明版本、分发请求、编写迁移脚本的完整实战流程并能理解 PostHog 为绝不静默迁移客户版本而设计的版本框架的底层原理。版本化机制是如何工作的PostHog 的每一个 Data Warehouse 导入源都是一个_BaseSource位于 sources/common/base.py的子类。源类通过以下四个类属性向框架声明自身的供应商 API 版本能力属性类型含义supported_versionstuple[str, ...]该源实现的全部供应商版本标签如 Stripe 日期版本、semver、名称。标签是不透明的框架从不解析、排序或比较它们。没有有意义的供应商版本时保持默认(v1,)即UNVERSIONED_API_VERSION见 base.py#L96-L98default_versionstr当源实例没有 pin 时使用的版本新建源时被打上的版本。默认也是UNVERSIONED_API_VERSION见 base.py#L174-L175api_docs_urlstr \| None供应商的 API 文档/变更日志页面——新版本在这里宣布。注意与SourceConfig.docsUrlposthog.com 上该源的使用文档页是两回事见 base.py#L177-L179deprecated_versionstuple[VersionDeprecation, ...]供应商已弃用的版本。VersionDeprecation是冻结 dataclass含version: str与sunset_at: datetime.date \| None见 base.py#L115-L120例如 Stripe 源sources/stripe/source.py#L112-L114supported_versions (STRIPE_API_VERSION_ACACIA,) default_version STRIPE_API_VERSION_ACACIA api_docs_url https://docs.stripe.com/changelogPin 的解析链路override → pin → default每条ExternalDataSource记录通过其api_version列固定pin一个版本NULL 时解析为default_version。此外单个 Schema 还可以携带用户管理的覆盖值ExternalDataSchema.api_version在该 Schema 的配置页面设置webhook 同步类 Schema 不可用它只对该 Schema 生效、优先于源级 pin。同步流水线在 workflow_activities/import_data_sync.py#L531 完成三层解析api_versionnew_source.resolve_api_version(schema.api_version or model.pipeline.api_version),解析后的结果以SourceInputs.api_version传入源实现——在源内部它永远是已解析的值绝不可能是 None。被 pin 的源在所有触点都使用同一版本一个被固定版本的源其版本不只影响同步时刻。源类上每个与供应商接触的方法都接收api_version: str | None None参数携带该源实例的已解析 pinNone→default_versionget_schemasSchema 发现validate_credentials凭证校验get_endpoint_permissions权限探测WebhookSource的管理方法create_webhook、sync_webhook_events、webhook_inputs_updated、get_external_webhook_info、delete_webhook持有源记录行的调用方创建、refresh_schemas、后台sync_new_schemas、webhook 端点、schema 级探测会传入已解析的 pin创建前的流程向导database_schema、一次性setup省略该参数自然解析为default_version——这也是新行被打上的版本。get_endpoint_permissions目前只有创建前调用方所以它的参数当前恒为None。各源内部负责根据版本构建 base-path/URL/header。刻意不做版本穿透纯映射或与版本无关的表面只有当真实供应商版本真正分化时才需要穿透包括get_desired_webhook_events/webhook_resource_map事件名映射、get_connection_metadata以及 github_warehouse_repos.py 中 GitHub 的按仓库 webhook 辅助函数。公开暴露与注册表不变量这些声明通过GET /api/public_source_configs/公开暴露字段versions、defaultVersion、apiDocsUrl、deprecatedVersions并通过源 API 按实例暴露api_version、api_version_deprecation实现见 presentation/views/external_data_source.py。api_versionpin 还可以通过 HogQL 的data_warehouse_sources系统表查询。注册表级不变量由 sources/tests/test_source_versions.py 对每一个已注册源强制校验default_version必须在supported_versions中且必须是最后一项即supported_versions按旧→新声明翻默认版本需在同一个 PR 里完成deprecated_versions⊆supported_versions默认版本永不弃用api_docs_url若存在必须以https://开头。test_resolve_api_version_honors_pin_and_falls_back_to_default还验证了核心语义None/空串回退到默认版本而任何已声明的 pin——哪怕是不再声明的旧标签——都被原样保留。第一步这个版本到底需不需要存在发现供应商出了个新标签并不是支持它的理由。动任何源文件之前先拿新版本与它所取代的版本——注意是源的当前default_version而不是supported_versions里的每一项——按领域逐项 diff依据来自供应商文档与变更日志。要对齐的是客户端实际发出的线上协议wire而不是标签本身UNVERSIONED_API_VERSION默认值v1不代表客户端打的是供应商最老的 API——一个在声明版本之前就建好的源可能已经在说现代协议此时给该 wire 补上真实标签只是纯声明式的重贴标签并重 pin而不是新请求路径。需要逐项对比的领域认证凭证字段、token/header 方案、scope、权限探测基础 URL、版本 header、每个资源实际提供的路径分页机制、参数、游标语义、页大小限制Schema 列表源暴露了哪些端点/表Schema 格式列、类型/格式、主键、增量字段webhook 载荷与订阅注册对WebhookSource限流、错误签名以及源请求层触碰的任何其他内容不加版本的情形什么也没变如果源所读取的内容在上述领域没有任何差异就不要加版本。保持supported_versions和default_version不动用逐领域、引用 changelog 的证据说明新标签与默认值在此处不可区分然后关闭任务。多加一个标签毫无收益只会带来成本一个用户可选的 pin、一套测试/API/UI 要永远维护的版本以及框架基于它分发这个隐含声明。必须加版本的情形以下任一成立才添加上述任何领域与基线产生分歧哪怕对我们的读取来说只是表面差异——那就分支处理。注意与更老的、仍在支持的标签的分歧不算数——那些 pin 无论如何都会继续服务各自的请求路径所以一个与默认值一致的新标签无论它离旧标签多远都是多余的供应商正在退役一个仍有行固定在上面的版本该标签即将失效——即使 wire 完全相同采纳新版本也是目的本身同时退役版本要移入deprecated_versions同一 PR源必须发送该标签才能获得它想要的行为必需的 header 或 URL 段——即该版本是一个请求输入而不仅仅是一个名字。什么也没变需要与出现分歧同样严格的文档证据。一份没读过的 changelog 不等于干净的 diff。以新版本自身的参考页为准要对新版本自己的参考页面逐个端点做 diff而不是对着 changelog 或文档 URL。供应商的新一代往往只覆盖其产品表面的一部分其 v2 文档段落会继续为一切 v2 未取代的东西打印 v1 请求行一个只读取未变动部分的源根本不需要新版本。同理一份按日期宣布退役个别端点的公告不是版本 sunset——在弃用某个版本或写重 pin 迁移之前先确认该版本本身真的停止服务了。逐步添加一个新版本阅读供应商 changelog源上的api_docs_url列出当前支持的版本与新版之间的变化改名/删除的字段、分页变化、新的必需 header、变化的 webhook 载荷或源读取的某个字段在新版中变成需要新查询参数才返回的 opt-in 字段旧版本默认返回、新版不请求就为空——通过在新版请求路径上加参数恢复它。验证仅靠文档没有存储的凭证、没有 live-sync 测试工具文档是每个版本提供什么的唯一事实来源。当供应商文档页是客户端渲染、抓下来是空壳时读它的机器可读镜像llms.txt、sitemap.md或同路径加.md后缀而不要把这个版本当成无文档。这也是上述 gate 所依据的证据。声明版本仅在 gate 判定该版本必须存在之后把新标签加入supported_versions并把default_version翻到它——新源总是从最新的稳定版本开始。被 pin 的行的同步路径不受默认翻转影响这正是 pinning 的意义但还有两件事会跟随新默认值若 pin 未在第 3 步穿透到 discovery/get_schemas以及任何api_version为 NULL 的行。请引用请求层的版本常量而不要复制字符串字面量。在请求层按SourceInputs.api_version分发保持最小化。如果版本只是一个 header/URL 段且响应形状兼容就把版本字符串向下穿透到构建 client/URL 的地方参考 StripeStripeSource.source_for_pipeline将self.resolve_api_version(inputs.api_version)传给stripe_source(...)→StripeClient(stripe_version...)。在源类上通过resolve_api_version解析——绝不要在请求层硬编码回退版本。只有行为真正分化时才引入按版本划分的模块/分支不同的分页、不同的字段映射。所有版本分支都必须留在源自己的目录里——绝不放进共享层。一个源可能对接多个按独立轨道版本化的供应商 API 家族因此源级 bump 可能只移动其部分端点并只改这些端点的认证——按已解析的 pin 对每个端点的分发路径和认证分别处理其余端点保持原 wire表集合保持一致而不是把所有端点都改写成新标签。可验证的子集也可以约束移动范围当新版本的 wire路径、认证、响应形状只对源的部分端点有文档时把这些端点移到新 wire其余留在新默认值下仍被服务的旧 wire——表集合保持一致不让任何端点带着猜出来的形状上线。因为供应商仍在服务旧路径而把整个 bump 卡住是错误的决定。当新版本重命名端点、改变主键或重塑响应时分化必须真的被分支处理——绝不能让旧的单版本请求路径继续服务新的默认值。所有相关表面都可以随版本变化get_rows通过inputs.api_version接收已解析的 pin凭证字段可以基于default_version判定。反过来不要添加惰性脚手架一个没有调用方会改变的api_version参数或值完全相同的版本→URL 映射是评审意见而不是前瞻兼容。只有在上面的 gate 因为非 wire 理由通过时仅声明只改supported_versions/default_version别的都不动才是正确形态——旧标签被退役、供应商在账户侧而非每次请求切换行为或源在框架的旧无版本标签下已经在读取供应商最新一代核实源实际构建的请求路径而不是标签——旧标签可能已经在走新 wire新标签只是为新行把它正式化两者解析结果相同。如果 gate 什么理由都没通过那就没有 PR。当无 header 请求解析到账户侧绑定的版本而非移动的 latest时穿一个版本 header 就不是惰性的——它会覆盖客户选择的版本这正是本框架要防止的静默迁移——所以保持仅声明、不发 header。Discovery 和探测路径收到 pin 就要消费它。框架把已解析的 pin 作为api_version参数传给get_schemas、validate_credentials、get_endpoint_permissions和 webhook 管理方法。多版本源必须从该参数构建其 discovery/probe/webhook client而不是从default_version或硬编码 header——否则被 pin 的源会在错误的版本下发现/对账其表可能消失、重复或对账失败。用self.resolve_api_version(api_version)解析它——持有行的调用方传入的已是解析值与SourceInputs.api_version一致所以源侧解析只覆盖传入None的创建前调用。只有在你能够说明该路径上版本为何无关紧要时忽略该参数才是正确的。留意依赖版本的列提示/schema例如 Stripe 的external_table_definitions是为特定版本构建的。添加响应形状不同的版本时把规范列提示限定到它们为之构建的版本让更新的版本从数据中自动推断 schema在应用提示处维护一组兼容提示的版本。对has_managed_hogql_schemaTrue的源这还包括读取路径hogql_definition的规范列映射是版本盲的所以被重命名的列也需要同步更新规范 schema/描述。让旧版本继续工作不要删除或改动此前支持版本的请求路径。移除版本是将来一个明确的决定不属于版本添加 PR 的一部分。测试扩展该源的测试让新旧版本都被覆盖——至少验证版本标签对每个支持的版本都到达 client/请求层mock 边界按版本参数化。注册表不变量测试会自动捕获声明错误。不要重复测试基类的resolve_api_version契约test_source_versions.py已覆盖所有源。当版本有分歧时从供应商文档按版本构造 fixture——一个 v1 形状的 mock 挂在 v2 pin 下证明不了任何事。一个源一个 PR。常规标题feat(warehouse_sources): support vendor API version label——scope 永远是warehouse_sources产品名而不是源目录/供应商名。弃用一个版本如果新版本尚未支持先按上面步骤实现。当被 sunset 的版本是供应商的终极版本整个产品停产、没有后继可加时按版本弃用无法适用——框架从不弃用一个源的唯一/默认版本也没有可重 pin 的版本。不要伪造后继或放宽不变量来强行造元数据保持supported_versions/default_version不变在 PR 中记录供应商 sunset 和客户退路在关停前整体迁离该源并把是否需要源级整体产品弃用表面抛给人类决策。把旧版本加入deprecated_versions带上供应商公布的 sunset 日期没有则sunset_atNone。永远不要弃用default_version——在同一个 PR 里把默认值翻到新版本。产品内警告横幅和 API 字段会自动从元数据亮起——零逐源 UI 工作。弃用 ≠ 迁移。已有 pin 只在供应商宣布版本将停止服务有 sunset/移除日期时才迁移。没有 sunset 日期的弃用是建议性的标记它让所有已有 pin 完全受支持不写迁移——把仍被供应商服务的版本上的正常工作客户重 pin恰恰是这个 pinning 框架要防止的静默版本迁移。两种窄情形在建议性sunset_atNone弃用下仍然重 pin被弃用标签解析为与新默认值字节级一致的请求纯别名——没有按版本分发——所以重 pin 不是迁移或供应商已对新版本之外的旧版本报错如410/406留着 pin 比迁移更糟。一个对供应商仍服务的版本发送逐版本 header/URL 的源两者都不是——保持建议性。仅对 sunset 中的版本包含一个已写好但绝不运行的迁移脚本把受影响的ExternalDataSource行api_version列从被弃用版本重 pin 到新版本外加任何安全的数据/schema 变换。它必须幂等、可评审且其逆操作必须是 no-op——重 pin 后的行与原生创建的行不可区分无差别降级会误伤合法的原生 pin。只重 pin 明确落在被弃用版本上的行当默认值已经是目标时你在本 PR 弃用一个旧标签但没有翻转默认值不要同时重 pin NULL pin——它们已经解析到当前默认值动它们是多余的 churn——也不要动其他仍被服务的被弃用版本。当迁移有损或不安全时——包括新版本需要无法从存储凭证推导出的凭证——不要脚本化在 PR 里记录手工路径。当只有一部分行不安全时版本在一个需要额外凭证的端点上分化拆分队列——为安全行脚本化重 pin通过查询其 schema 排除不安全行只把那一队记为手工——不要因为一个端点无法迁移就把整个迁移降级为手工。不要执行迁移或回填由人类评审并运行。绝不在迁移脚本中触碰ExternalDataSchema.api_version覆盖值——它们按设计由用户管理。Schema 级弃用警告覆盖它们用户从该 Schema 的配置页面自行迁移。Pinning 语义不要破坏这些source.resolve_api_version(pinned)原样尊重存在的 pin——哪怕是已不再声明的 pin——因为静默把客户移到另一个版本正是本框架要防止的失败模式。空串/NULL 回退到源类自己的default_version见 base.py#L207-L215。API 创建路径_create_external_data_source位于 presentation/views/external_data_source.py打上default_version印记迁移0075_backfill_externaldatasource_api_version回填了既存行——所以大多数行带有具体 pin。但api_version可空绕过印记的直接 ORM 创建路径如seed_engineering_analytics.py以及任何未来的 seeder/backfill/脚本可能留下 NULL而 NULL pin 解析到default_version——所以它会跟随默认翻转。不要笼统声称每行都被 pin 了翻转是安全的核实该源真实的 pin 状态如果可能存在 NULL 队列要么用写好不运行的迁移把它处理掉要么确认各版本请求字节级一致。重 pin 客户 更新ExternalDataSource.api_version支持 runbookUpdating a warehouse source to a new vendor API version位于 PostHog/runbooks 仓库。常见陷阱清单供应商版本标签是不透明的2026-02-25.clover、v21.0、2022-06-28。原样复制绝不规范化、排序或解析。源内的逐端点 URL 版本端点配置里硬编码的/v2/...、/v3/...独立于框架的源级版本标签。一个源可能已经在调用供应商最新的逐资源路由同时仍携带UNVERSIONED_API_VERSION默认值——所以即使供应商自己的版本号看起来相差很远加版本也可能正确且仅需声明。diff 的是源实际请求的内容而不是供应商的标题版本。供应商用逐端点查询参数选版本、且部分端点无论版本都强制要求它时那些端点在每个 pin 下都必须发送选择器包括旧 pin——它们与版本无关尽管选择器值与新标签相同按已解析 pin 门控它们会破坏旧 pin。只在选择器可选、仅增强响应的端点上按 pin 门控新版本下的额外字段那才是真正的分化。供应商称为breaking的变化如资源 id 从 int 迁移到 string仍可能不需要按版本分支——当源只是不透明地透传受影响值主键的列名稳定、类型自动推断、游标原样转发时。版本仍必须存在gate 因真实分化而通过但只在变化击中你硬编码的表面处分支请求路径列提示、解析过的游标、带类型的主键。版本 bump 常常也改变webhook 载荷——如果源是WebhookSource检查 webhook 创建的 client创建于源设置时刻而非同步时刻是否也需要版本以及既有 webhook 订阅是否需要更新。凭证校验路径validate_credentials、权限探测在创建时刻运行、没有行 pin它们可能使用默认/旧版本。每次版本 bump 是否改它们是可选的——切换之前先验证供应商在新版本下接受这些校验调用。凭证探测通过 ≠ 同步能跑探测只打一个端点get_rows打其余端点当它们按版本分化时探测通过而每个表都 404。版本→header/路径映射必须覆盖每个受支持标签——.get()的 fallthrough 会静默不发版本 header追踪 latest即本框架防止的漂移。断言全覆盖或直接 raise。首次为今天不发送版本选择器的源做版本化让既有标签UNVERSIONED_API_VERSION默认继续什么都不发只对新日期标签加选择器。这逐字节保留已 pin 的行而 pin 到新默认值正是目的——无选择器路径此前在追踪供应商账户配置的版本那就是漂移。这不是上面的 fallthrough bug这里的空选择器是有意为之、属于某一个特定旧标签而不是.get()漏配。并行的版本 bump PR 会抢占同一个迁移号第二个合入的会变成冲突叶并被ci:preflight拦截。检查max_migration.txt并重新编号。不要为既有客户重新生成 schema作为版本添加的一部分schema 变化只适用于通过人类运行的迁移重 pin 的行。SOURCE pin 下的 discovery diffsync_new_schemas、refresh_schemas、bulk sync-defaults。一个 schema 级api_version覆盖值若所在版本的表集合与源的版本不同可能被该 diff 禁用/软删除——把覆盖值限制在短期验证窗口内不要作为长期把某张表钉在另一版本上的手段。当版本化源在任何供应商调用上发送 pindiscovery或sync——静态端点目录在 discovery 时不消费它但get_rows仍发送版本 header时把供应商的版本拒绝错误签名如406/410加进get_non_retryable_errors否则退役的 pin 会把重试节奏变成永久的错误循环且没有用户可见表面。版本 bump 可能改变认证方案而不只是 wire 格式。此时源配置需要两种凭证形态都做成可选字段认证构造按已解析版本分发validate_credentials强制校验该版本所需的那一对——表单级required表达不了取决于 pin。新版本可能把旧版本一次性返回完的地平线改成分页固定记录上限加next/prev链接。对快照轮询型源保持每次同步一个请求、不要跟随链接——供应商按页计费——并在每张表的描述中注明所得地平线因为它可能短于旧版本的。版本改变分页机制如 offset→cursor也会改变可恢复检查点持久化的形状。给 resume-config dataclass 加一个有默认值的新字段而不是复用一个既有字段——这样旧版本下持久化的状态仍可解码——并让 seed 与保存检查点逻辑按已解析版本分发。跨不兼容机制复用一个字段会破坏另一个版本的进行中恢复。供应商在版本间重命名集合时跨版本保持 schema/表名集合一致把重命名放进端点配置的按版本路径——否则 discovery diff 会在重 pin 时孤立该表。新版本对旧端点没有对应物集合被砍掉而非重命名时只让该表出现在服务它的版本上、允许表集合随版本不同——绝不为了集合相等而把它映射到猜出来的路径因为文档是唯一事实来源未经验证的路径会让新源暴露一张在同步时 404 的表。无分发的源可能从共享模型上的常量读取版本如 OAuthIntegration模型该常量也驱动与版本无关的流程如 OAuth token 铸造。只把同步请求路径重指向已解析 pin留下该常量因为 bump 它会把其他流程的版本一起改掉爆炸半径超出本源。当新版本是真正不同的供应商产品而非重命名集合时新旧标签服务不同的端点和表给新版本独立的不相交表集合不要强行套旧名字。这种分化之所以安全正因为源从不被重 pin——所以 2.5→3.0 式的迁移是有损的其迁移是文档化而非脚本化与改变主键的重命名完全一致。共享 REST 框架只把解析后的父字段绑定到子资源 URL 的路径而不是查询字符串_bind_path_params会抛 Resolve query params not supported yet。如果新版本用查询参数限定 fan-out 子资源、而旧版本用路径段依赖资源的{type: resolve}机制表达不了它——在源里显式遍历父 id把每个 id 烘进子资源的参数或 POST body。自改进原则一个 PR 的默认结局是这个 Skill 文档不变。只有当某个经验同时通过三道门槛时才值得修改它能在多个源间泛化、会改变未来 Agent 的行为、且尚未被上文各节陈述或可推导。供应商 changelog 细节、逐源分发链或代码路径、测试细节永远不够格——那些上下文属于你的 PR不属于这里。够格时把经验折叠成某节gate、某一步、某条陷阱里一句供应商中立的描述不要在本文件中追加经验列表、changelog 或带日期的笔记。延伸阅读想从零了解该导入框架可先读 sources/common/base.py版本声明与resolve_api_version契约、sources/common/registry.py源注册表、sources/SOURCES.md源目录总览以及 sources/tests/test_source_versions.py注册表不变量测试。【免费下载链接】posthog:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.项目地址: https://gitcode.com/GitHub_Trending/po/posthog创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考