本文转载自微信公众号《黑客下午茶》,作者少。转载本文请联系黑客下午茶公众号。探索Snuba数据模型为了构建Snuba查询,第一步是能够知道您应该查询哪个数据集、您应该选择哪些实体以及每个实体的模式是什么。有关数据集和实体的介绍,请参阅Snuba数据模型部分。https://getsentry.github.io/snuba/architecture/datamodel.html数据集可以在这个模块中找到。每个数据集都是一个引用实体的类。https://github.com/getsentry/snuba/blob/master/snuba/datasets/factory.py通过snuba实体命令可以找到系统中的实体列表:snubaentitieslist会返回如下内容:DeclaredEntities:discovererroreventsgroupsgroupassigneegroupedmessage。....Once既然我们已经找到了我们感兴趣的实体,我们需要了解在该实体上声明的模式和关系。同样的命令描述一个实体:snubaentitiesdescribegroupedmessage返回:EntitygroupedmessageEntityschema----------------------------offsetUInt64record_deletedUInt8project_idUInt64idUInt64statusNullable(UInt8)last_seenNullable(DateTime)first_seenNullable(DateTime)active_atNullable(DateTime)first_release_idNullable(UInt64)关系--------------------------------组----------------------------目的地:事件类型:LEFTJoinkeys-------------------------------project_id=LEFT.project_idid=LEFT.group_id提供列列表及其类型以及与数据模型中定义的其他实体的关系。准备针对Snuba的查询Snuba查询语言称为SnQL。它记录在SnQL查询语言部分。所以本节不再赘述。https://getsentry.github.io/snuba/language/snql.html有一个pythonsdk可用于构建Snuba查询,它可以与任何Python客户端一起使用,包括Sentry。snuba-sdk。https://github.com/getsentry/snuba-sdkQuery表示为一个Query对象,如:query=Query(dataset="discover",match=Entity("events"),select=[Column("title"),Function("uniq",[Column("event_id")],"uniq_events"),]groupby=[Column("title")],where=[Condition(Column("timestamp"),Op.GT,datetime.datetime(2021,1,1)),Condition(Column("project_id"),Op.IN,Function("tuple",[1,2,3])),],limit=Limit(10),offset=Offset(0),granularity=Granularity(3600),)有关如何构建查询的更多详细信息,请参阅sdk文档。https://getsentry.github.io/snuba-sdk/一旦查询对象准备就绪,就可以将其发送到Snuba。使用Sentry向Snuba发送查询查询Snuba的最常见用例是通过Sentry。本节介绍如何在Sentry代码库中构建查询并将它们发送到Snuba。Sentry导入了上面的Snubasdk。这是构造Snuba查询的推荐方法。一旦创建了Query对象,Sentry提供的Snuba客户端api就可以而且应该用于向Snuba发送查询。api在此模块中。它负责缓存、重试并允许批处理查询。https://github.com/getsentry/sentry/blob/master/src/sentry/utils/snuba.py#L667此方法返回包含响应中的数据和其他元数据的字典:{"data":[{"title":"verybad","uniq_events":2}],"meta":[{"name":"title","type":"String"},{"name":"uniq_events","type":"UInt64"}],"timing":{...details...}}数据部分是一个列表,每行一个字典。meta包含响应中包含的列列表,其数据类型由Clickhouse推断。通过WebUI发送测试查询Snuba有一个可用于发送查询的最小WebUI。您可以在本地运行Snuba并通过http://localhost:1218/[DATASETNAME]/snql访问WebUI。SnQL查询应在查询属性中提供,响应的结构与上一节中讨论的相同。通过curl向WebUI发送查询只会将负载作为POST发送。因此,使用curl或任何其他HTTP客户端可以实现相同的结果。请求和响应格式请求格式如上图所示:query包含字符串形式的SnQL查询。dataset是数据集名称(如果在url中尚未指定。调试会导致Snuba在响应中提供详尽的统计信息,包括Clickhouse查询。consistent强制Clickhouse查询以单线程模式执行,如果复制Clickhouse表,它将forceSnubatoAlwayshitsthesamenode.保证顺序一致性,因为这是消费者默认写入的节点。这是通过将负载平衡Clickhouse属性设置为in_order来实现的。https://clickhouse.tech/docs/en/operations/settings/settings/#load_balancing-in_orderturbo为TURBO_SAMPLE_RATESnuba设置中定义的查询设置采样率。它还可以防止Snuba将FINAL模式应用于Clickhouse查询,以防在替换后需要保证正确的结果。Snuba可以用4个http代码响应。200表示查询成功,如果无法正确验证查询则为400。500通常意味着与Clickhouse相关的问题(从超时到连接问题),尽管Snuba仍然无法提前识别一些无效查询。Snuba有一个内部速率限制器,所以429也是一个可能的返回码。成功查询的响应格式与上面讨论的相同。完整版本如下所示(调试模式下){"data":[],"meta":[{"name":"title","type":"String"}],"timing":{"timestamp":1621038379,"duration_ms":95,"marks_ms":{"cache_get":1,"cache_set":4,"execute":39,"get_configs":0,"prepare_query":10,"rate_limit":4"validate_schema":34}},"stats":{"clickhouse_table":"errors_local","final":false,"referrer":"http://localhost:1218/events/snql","sample":空,“project_rate”:0,“project_concurrent”:1,“global_rate”:0,“global_concurrent”:1,“consistent”:false,“result_rows”:0,“result_cols”:1,“query_id”:“f09f3f9e1c632f395792c6a4bfe7c4fe"},"sql":"SELECT(titleAS_snuba_title)FROMerrors_localPREWHEREequals((project_idAS_snuba_project_id),1)WHEREquals(deleted,0)ANDgreaterOrEquals((timestampAS_snuba_timestamp),toDateTime('2021-05-01T00:00:00','Universal'))ANDless(_snuba_timestamp,toDateTime('2021-05-11T00:00):00','Universal'))LIMIT1000OFFSET0"}计时部分包含查询的时间戳和持续时间有趣的是,持续时间被分解为阶段:marks_ms。sql元素是Clickhouse查询。stats字典包含以下键clickhouse_table是snuba在查询处理过程中选择的表final表示Snuba是否决定向Clickhouse发送FINAL查询,强制Clickhouse立即应用相关合并(MergeTree)详情https://clickhouse.tech/docs/en/sql-reference/statements/select/from/#select-from-finalsample是应用的采样率。project_rate是Snuba在查询时针对特定项目每秒接收的请求数。project_concurrent是并发查询数涉及特定的查询时的c项目。global_rate与project_rate相同,但不专注于一个项目。global_concurrent与project_concurrent相同,但不专注于一个项目。query_id是此查询的唯一标识符。查询验证问题通常采用以下格式:{"error":{"type":"invalid_query","message":"missing>=conditiononcolumntimestampforentityevents"}}Clickhouse错误将具有类似的结构。类型字段将显示clickhouse,消息将包含有关异常的详细信息。与查询验证错误相反,在Clickhouse错误的情况下,查询实际上被执行了,因此有为成功查询描述的所有时间和统计信息。
