Sentry 監(jiān)控 - Snuba 數(shù)據(jù)中臺架構(gòu)(編寫和測試 Snuba 查詢)
本文轉(zhuǎn)載自微信公眾號「黑客下午茶」,作者為少 。轉(zhuǎn)載本文請聯(lián)系黑客下午茶公眾號。
探索 Snuba 數(shù)據(jù)模型
為了構(gòu)建 Snuba 查詢,第一步是能夠知道您應該查詢哪個數(shù)據(jù)集,您應該選擇哪些實體以及每個實體的 schema 是什么。
有關數(shù)據(jù)集和實體的介紹,請參閱 Snuba 數(shù)據(jù)模型部分。
- https://getsentry.github.io/snuba/architecture/datamodel.html
數(shù)據(jù)集可以在這個模塊中找到。每個數(shù)據(jù)集都是一個引用實體的類。
- https://github.com/getsentry/snuba/blob/master/snuba/datasets/factory.py
系統(tǒng)中的實體列表可以通過 snuba entity 命令找到:
- snuba entities list
會返回如下內(nèi)容:
- Declared Entities:
- discover
- errors
- events
- groups
- groupassignee
- groupedmessage
- .....
一旦我們找到了我們感興趣的實體,我們就需要了解在該實體上聲明的 schema 和 relationship。相同的命令描述了一個實體:
- snuba entities describe groupedmessage
會返回:
- Entity groupedmessage
- Entity schema
- --------------------------------
- offset UInt64
- record_deleted UInt8
- project_id UInt64
- id UInt64
- status Nullable(UInt8)
- last_seen Nullable(DateTime)
- first_seen Nullable(DateTime)
- active_at Nullable(DateTime)
- first_release_id Nullable(UInt64)
- Relationships
- --------------------------------
- groups
- --------------------------------
- Destination: events
- Type: LEFT
- Join keys
- --------------------------------
- project_id = LEFT.project_id
- id = LEFT.group_id
它提供列的列表及其類型以及與數(shù)據(jù)模型中定義的其他實體的關系。
準備對 Snuba 的查詢
Snuba 查詢語言稱為 SnQL。它記錄在 SnQL 查詢語言部分。所以本節(jié)不贅述。
- https://getsentry.github.io/snuba/language/snql.html
有一個 python sdk 可用于構(gòu)建 Snuba 查詢,它可以用于任何 Python 客戶端,包括 Sentry。snuba-sdk。
- https://github.com/getsentry/snuba-sdk
查詢表示為一個 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),
- )
有關如何構(gòu)建查詢的更多詳細信息,請參見 sdk 文檔。
- https://getsentry.github.io/snuba-sdk/
一旦查詢對象準備就緒,它就可以發(fā)送到 Snuba。
使用 Sentry 向 Snuba 發(fā)送查詢
查詢 Snuba 時最常見的用例是通過 Sentry。本節(jié)說明如何在 Sentry 代碼庫中構(gòu)建查詢并將其發(fā)送到 Snuba。
Sentry 導入了上述的 Snuba sdk。這是構(gòu)建 Snuba 查詢的推薦方法。
一旦創(chuàng)建了 Query 對象,Sentry 提供的 Snuba client api 就可以并且應該用于將查詢發(fā)送到 Snuba。
api 在這個模塊中。它負責緩存、重試并允許批量查詢。
- https://github.com/getsentry/sentry/blob/master/src/sentry/utils/snuba.py#L667
該方法返回一個字典,其中包含響應中的數(shù)據(jù)和其他元數(shù)據(jù):
- {
- "data": [
- {
- "title": "very bad",
- "uniq_events": 2
- }
- ],
- "meta": [
- {
- "name": "title",
- "type": "String"
- },
- {
- "name": "uniq_events",
- "type": "UInt64"
- }
- ],
- "timing": {
- ... details ...
- }
- }
data 部分是一個列表,每行一個字典。meta 包含響應中包含的列的列表,其數(shù)據(jù)類型由 Clickhouse 推斷。
通過 Web UI 發(fā)送測試查詢
Snuba 具有可用于發(fā)送查詢的最小 Web UI。您可以在本地運行 Snuba, 并且可以通過 http://localhost:1218/[DATASET NAME]/snql 訪問 Web UI。
應該在 query 屬性中提供 SnQL 查詢,并且響應的結(jié)構(gòu)與上一節(jié)中討論的相同。
通過 curl 發(fā)送查詢
Web UI 僅將 payload 作為 POST 發(fā)送。因此,使用 curl 或任何其他 HTTP 客戶端可以實現(xiàn)相同的結(jié)果。
請求和響應格式
請求格式在上面截圖中可見:
- query 包含字符串形式的 SnQL 查詢。
- dataset 是數(shù)據(jù)集名稱(如果尚未在 url 中指定。
- debug 使 Snuba 在響應中提供詳盡的統(tǒng)計信息,包括 Clickhouse 查詢。
- consistent 強制 Clickhouse 查詢以單線程模式執(zhí)行,并且如果 Clickhouse 表被復制,它將強制 Snuba 始終命中同一個節(jié)點??梢员WC順序一致性,因為這是消費者默認寫入的節(jié)點。這是通過設置為 in_order 的負載平衡 Clickhouse 屬性實現(xiàn)的。
- https://clickhouse.tech/docs/en/operations/settings/settings/#load_balancing-in_order
- turbo 為 TURBO_SAMPLE_RATE Snuba 設置中定義的查詢設置采樣率。它還可以防止 Snuba 將 FINAL 模式應用于 Clickhouse 查詢,以防在替換后需要保證正確的結(jié)果。
Snuba 可以使用 4 個 http code 進行響應。200 表示成功的查詢,如果查詢無法正確驗證,則為 400。500 通常意味著與 Clickhouse 相關的問題(從超時到連接問題),盡管 Snuba 仍然無法提前識別一些無效查詢。Snuba 有一個內(nèi)部速率限制器,所以 429 也是一個可能的返回碼。
成功查詢的響應格式與上面討論的相同。完整版本如下所示(在 debug 模式下)
- {
- "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": null,
- "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 (title AS _snuba_title) FROM errors_local PREWHERE equals((project_id AS _snuba_project_id), 1) WHERE equals(deleted, 0) AND greaterOrEquals((timestamp AS _snuba_timestamp), toDateTime('2021-05-01T00:00:00', 'Universal')) AND less(_snuba_timestamp, toDateTime('2021-05-11T00:00:00', 'Universal')) LIMIT 1000 OFFSET 0"
- }
timing 部分包含查詢的時間戳和持續(xù)時間。有趣的是,持續(xù)時間被分解為幾個階段:marks_ms。
sql 元素是 Clickhouse 查詢。
stats 字典包含以下 key
- clickhouse_table 是 snuba 在查詢處理過程中選取的表。
- final 表示 Snuba 是否決定向 Clickhouse 發(fā)送 FINAL 查詢,這會迫使 Clickhouse 立即應用相關的合并(Merge Tree)。細節(jié)
- https://clickhouse.tech/docs/en/sql-reference/statements/select/from/#select-from-final
- sample 是應用的采樣率。
- project_rate 是查詢時 Snuba 每秒收到的特定項目的請求數(shù)。
- project_concurrent 是查詢時涉及特定項目的并發(fā)查詢數(shù)。
- global_rate 與 project_rate 相同,但不專注于一個項目。
- global_concurrent 與 project_concurrent 相同,但不專注于一個項目。
- query_id 是此查詢的唯一標識符。
查詢驗證問題通常采用以下格式:
- {
- "error": {
- "type": "invalid_query",
- "message": "missing >= condition on column timestamp for entity events"
- }
- }
Clickhouse 錯誤將具有類似的結(jié)構(gòu)。type 字段將顯示 clickhouse,該消息將包含有關異常的詳細信息。與查詢驗證錯誤相反,在 Clickhouse 錯誤的情況下,實際執(zhí)行了查詢,因此存在為成功查詢描述的所有時間和統(tǒng)計信息。