Metrics without Limits และ Tag Cardinality
ใจความสำคัญในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ใจความสำคัญในหนึ่งประโยค”ตัวขับ cost จริงของ custom metric คือ tag cardinality จำนวน combination ของ tag-value ที่ถูก index และ query ได้ ไม่ใช่จำนวน data point ดิบที่ ingest เข้ามา และ proof จริงของบทก่อนหน้าที่เห็น time series แยก 30 ตัว คือสิ่งที่ Metrics without Limits™ ของบทนี้มีไว้ให้กู้คืนได้โดยไม่ต้อง re-instrument
Cardinality คือ cost ไม่ใช่ volume
หัวข้อที่มีชื่อว่า “Cardinality คือ cost ไม่ใช่ volume”หลายคนคิดว่า cost ของ metric เหมือน log หรือ trace คือยิ่ง data point เยอะยิ่งแพง แต่ custom metric ไม่ได้ทำงานแบบนั้นเป๊ะ ๆ ตาม docs.datadoghq.com/account_management/billing/custom_metrics/: “A custom metric is uniquely identified by a combination of a metric name and tag values (including the host tag)” — ตรงกับหน่วยเดียวกับที่บทก่อนหน้าวัดว่าระเบิดเป็น 30 แถวแยกจาก metric name เดียวกับ tag ที่ไม่ซ้ำกันต่อ call ตัวเดียว การคิด billing เองก็คำนวณแบบเดียวกัน: “Your monthly billable custom metrics usage is calculated by taking the total of all distinct custom metrics (also known as timeseries) for each hour in a given month, and dividing it by the number of hours in the month” — และที่สำคัญ “Your billable usage is not impacted by data point submission frequency or the number of queries you run on your metrics.” ความถี่ในการ submit ฟรี แต่จำนวน series ที่แยกกันไม่ฟรี
Metrics without Limits™ ทำอะไรจริง ๆ
หัวข้อที่มีชื่อว่า “Metrics without Limits™ ทำอะไรจริง ๆ”Metrics without Limits™ (ชื่อ product ที่จดทะเบียนเครื่องหมายการค้า ตาม docs.datadoghq.com/metrics/metrics-without-limits/) แยก “ส่ง metric นี้ด้วย tag อะไรบ้าง” ออกจาก “tag ไหนถูก index และ query ได้จริงสำหรับ metric นี้ใน Datadog” ยกคำพูดตรง ๆ: “Decouple custom metric ingestion and indexing to control volumes and costs by selecting which tags remain queryable.” docs หน้าเดียวกันตั้งชื่อสอง volume ที่มันแยก billing ออกเป็น: Ingested Custom Metrics (“the original volume of custom metrics based on all ingested tags”) และ Indexed Custom Metrics (“the volume of custom metrics that remains queryable in the Datadog platform”) ในทางปฏิบัติคือ ยังส่ง metric พร้อม tag cardinality สูงอย่าง pod_name จาก application หรือ DogStatsD client ได้ต่อไป — instrumentation ไม่ต้องเปลี่ยนอะไรเลย และตัวอย่าง request_id จริงของบทก่อนหน้าคือ tag ประเภทเป๊ะ ๆ ที่ feature นี้ถูกสร้างมาเพื่อควบคุมย้อนหลัง — แล้วไป config metric ตัวนั้นเฉพาะให้ index แค่ subset ของ tag ที่เลือกไว้ เช่น service กับ env Datadog ยังคง ingest ค่าดิบเหมือนเดิม แต่ time series ที่ query ได้ของ metric นั้นจะยุบลงเหลือแค่ combination cardinality ต่ำตามที่ config ไว้ และนั่นคือสิ่งที่ถูกคิด billing ฝั่ง indexed
จุดสำคัญคือ config นี้เป็นแบบ ต่อ custom metric และ ไม่ทำลายข้อมูลและย้อนกลับได้ ถ้าอีกไม่กี่สัปดาห์ต่อมากำลัง debug ปัญหาแล้วต้องการแบ่ง metric ตัวเดียวกัน by pod_name เพื่อสืบสวนลึกขึ้น ก็เปิด tag นั้นกลับมาให้ query ได้ — ไม่ได้หายไปตลอดกาลเพียงเพราะเมื่อวานไม่ได้ index ไว้ และไม่ต้องแตะ application code หรือ deploy ใหม่เพื่อเปลี่ยนแปลงนี้เลย
Metric: myservice.request.durationSubmitted tags: env, service, pod_name, availability_zoneIndexed tags (configured today): env, service -> queryable combinations: small, low costIndexed tags (turned on later, for an investigation): env, service, pod_name -> queryable combinations: large, higher cost, but only while you need itJSON metric_tag_configuration validate กับ model จริง
หัวข้อที่มีชื่อว่า “JSON metric_tag_configuration validate กับ model จริง”shape ที่ API/Terraform จัดการ config ของ Metrics without Limits คือ MetricTagConfigurationCreateRequest ยืนยันว่ามีอยู่จริงในชื่อนี้เป๊ะภายใต้ module datadog_api_client.v2 ที่ติดตั้งอยู่ (ไม่ใช่ v1 — resource นี้มีแค่ใน v2) ตัวอย่างจริง validate แล้ว:
// myservice-request-duration-tags.json -- metric_tag_configurations is not// one of this harness's collected --json directories (monitors/dashboards/// slos/service-catalog/logs-pipelines only); validated by hand below against// datadog_api_client.v2.model.metric_tag_configuration_create_request.{ "data": { "type": "metric_tag_configurations", "id": "myservice.request.duration", "attributes": { "metric_type": "distribution", "tags": ["env", "service"], "include_percentiles": true, "exclude_tags_mode": false } }}$ python3 -c "from datadog_api_client.v2.model.metric_tag_configuration_create_request import MetricTagConfigurationCreateRequestMetricTagConfigurationCreateRequest(**data, _check_type=True)print('OK: valid')"OK: validสอง error จริงที่จับได้ ควรรู้ไว้ก่อนที่จะเสียเวลา debug — ทั้งคู่เป็นความผิดพลาดที่ AI-generated code มีโอกาสทำได้จริง เพราะนี่คือ envelope แบบ JSON:API:
# ขาด "id" -- ผิดพลาดได้ธรรมชาติมาก เพราะชื่อ metric อ่านดูเหมือนควรเป็น# field "attributes.name" ไม่ใช่ "id" แบบ JSON:API$ python3 -c "from datadog_api_client.v2.model.metric_tag_configuration_create_request import MetricTagConfigurationCreateRequestMetricTagConfigurationCreateRequest(data={'type': 'metric_tag_configurations', 'attributes': {...}}, _check_type=True)"TypeError: MetricTagConfigurationCreateData.__init__() missing 1 required positional argument: 'id'# "tags" เป็น string คั่นด้วย comma แทนที่จะเป็น JSON array -- สับสนได้ง่าย# กับ syntax tag-filter query ของ Datadog เองที่คั่นด้วย comma$ python3 -c "...MetricTagConfigurationCreateRequest(data={..., 'attributes': {'tags': 'env,service', ...}}, _check_type=True)"ApiTypeError: Invalid type for variable 'tags'. Required value type is list and passed type was str at ['data']['attributes']['tags']Percentile aggregation เป็น opt-in และปลดล็อกชุดที่กว้างขึ้นเมื่อขอเท่านั้น
หัวข้อที่มีชื่อว่า “Percentile aggregation เป็น opt-in และปลดล็อกชุดที่กว้างขึ้นเมื่อขอเท่านั้น”field include_percentiles ใน JSON ด้านบนเป็น field จริง มีอยู่จริงบน model และคุมสิ่งที่ document ไว้ชัดเจนสำหรับ metric DISTRIBUTION ที่ docs.datadoghq.com/metrics/distributions/: โดย default distribution metric มีแค่ count, min, max, sum และ avg เป็น aggregation ตอน query การ opt-in (ตาม docs ปัจจุบันทำผ่านหน้า Metrics Summary ใน UI หรือ field include_percentiles ที่เห็นตรงนี้ผ่าน API/Terraform) ปลดล็อก “p50, p75, p90, p95, and p99 or any user-defined percentile of your choosing (with up to two decimal points such as 99.99).” นี่คือไอเดียเดียวกับ “เลือก aggregation ตอน query ไม่ใช่ตอน submit” ที่บท DogStatsD แนะนำไว้สำหรับ DISTRIBUTION — ความพร้อมใช้งานของ percentile เองเป็น opt-in แยกอีกชั้นบนนั้น ไม่ใช่อัตโนมัติแค่เพราะ metric เป็น DISTRIBUTION
Percentile aggregation ตอน query
หัวข้อที่มีชื่อว่า “Percentile aggregation ตอน query”อีกแนวคิดที่เกี่ยวข้องและมักทำให้สับสนคู่กับ cardinality คือสำหรับ metric type DISTRIBUTION ไม่ได้ล็อก “avg” หรือ “p95” ไว้ตอน submit — เลือก aggregation ตอนที่ query หรือ graph metric จริง ๆ ต่างหาก (เมื่อ percentile ถูกปลดล็อกตาม section ด้านบนแล้ว) ข้อมูล distribution ชุดเดียวกันดูเป็น avg, max, p50, p95, หรือ p99 ได้ตามที่เลือกใน query editor หรือ dashboard widget โดยไม่ต้อง resubmit อะไรเลย นี่คือคนละมิติกับ tag cardinality cardinality ควบคุมว่า tag combination ไหน query ได้ ส่วน aggregation ควบคุมว่าค่าตัวเลขภายใน combination ที่เลือกไว้ถูกสรุปยังไงตอนที่ดู
flowchart LR
A[App / DogStatsD submits high-cardinality tags: pod_name, az, env, service] --> B[Raw ingestion: cheap, all tags accepted]
B --> C{Metrics without Limits config}
C -->|indexed today: env, service| D[Queryable time series: low cardinality, lower cost]
C -->|can enable later: + pod_name| E[Queryable time series: higher cardinality, for deep investigation] Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”🐛 หา bug ในโค้ดนี้
// AI comment: "Configure Metrics without Limits for myservice.request.duration"{"data": { "type": "metric_tag_configurations", "attributes": { "metric_type": "distribution", "tags": "env,service", "include_percentiles": true }}}เฉลย
ดูสมเหตุสมผลแวบแรก — envelope ระดับบนสุดที่ shape ถูกต้อง, metric type จริง, value ของ tags ที่ดูน่าเชื่อ สอง bug จริง ทั้งคู่พิสูจน์ด้วย model จริงด้านบน: อันดับแรก data.id — ชื่อ metric เอง — หายไปทั้งหมด; ใน payload แบบ JSON:API นี้ชื่อ metric คือ resource id ไม่ใช่ attribute และ client จริงโยน TypeError: ... missing 1 required positional argument: 'id' ทันที อันดับสอง "tags": "env,service" คือ string คั่นด้วย comma ไม่ใช่ JSON array — ผิดพลาดได้ธรรมชาติมากเพราะ syntax tag-filter query ของ Datadog เองที่อื่นใช้ comma คั่น แต่ field นี้ต้องการ JSON array จริง (["env", "service"]) และ client จริงโยน ApiTypeError จริงที่ระบุ field และ path ตรงตัว ทั้งคู่คือความล้มเหลวจริงที่จับได้ทันที — ไม่ใช่ misconfiguration แบบเงียบ ๆ ซึ่งเป็นข่าวดีอย่างเดียวตรงนี้
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”ลองนึกภาพ submission ดิบของ custom metric เป็น สินค้าที่มาถึงท่าเรือ และ Metrics without Limits เป็น ศุลกากรที่ตัดสินว่าอะไรเข้าคลังสินค้าเทียบกับแค่บันทึกแล้วปล่อยผ่าน ทุกอย่างที่มาถึงถูกตรวจและนับ (ingestion — ฟรี ตามคำพูดจาก billing ด้านบน ไม่ว่าสินค้าจะมาถึงบ่อยแค่ไหน) แต่มีแค่สิ่งที่ศุลกากรตัดสินใจเก็บเข้าคลัง (index) เท่านั้นที่กินพื้นที่จัดเก็บที่ต้องจ่ายเงิน และกลายเป็นสิ่งที่ดึงออกมา query ได้จริงทีหลัง การเปลี่ยนสิ่งที่ศุลกากรเก็บเข้าคลังพรุ่งนี้ไม่ต้องให้ผู้ส่งเปลี่ยนอะไรเกี่ยวกับสิ่งที่ส่งเลย — เป็นการตัดสินใจที่ชายแดนทั้งหมด หลังจากที่มาถึงแล้ว และย้อนกลับได้ทั้งสองทาง
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”- Config Metrics without Limits แบบ proactive เทียบกับ reactive — config cardinality ต่ำตั้งแต่วันแรกทำให้ cost ที่ index ทำนายได้ตั้งแต่ต้น แต่ต้องเดาให้ถูกว่า tag ไหนจะไม่ต้อง query เลย; ปล่อยให้ index ทุกอย่างแล้วค่อยลดทีหลังปลอดภัยกว่าสำหรับระบบที่ยังไม่เข้าใจดี แต่เสี่ยงที่ cost ที่ index จะพุ่งขึ้นจริงก่อนที่ใครจะสังเกตเห็น
include_percentiles: trueเทียบกับชุด aggregation default — ชุด defaultcount/min/max/sum/avg(พิสูจน์ด้านบนผ่าน docs) ครอบ dashboard ส่วนใหญ่แบบถูก; การ opt-in percentile ปลดล็อก queryp95/p99ระดับ global ที่แม่นยำ ซึ่งมักเป็นจุดประสงค์จริงของการเลือกDISTRIBUTIONเหนือHISTOGRAMตั้งแต่แรก แลกกับ cost เพิ่มเติมที่ pricing ปัจจุบันของ Datadog ผูกไว้ด้วย (claim ที่บทนี้ไม่พูดโดยไม่มี URL ตามกฎของคอร์สนี้เอง)- การ validate แบบ strict-type-แต่-loose-enum ของ client นี้ (พิสูจน์ด้านบน) เทียบกับการ call API จริง — การ validate แบบ local offline ที่บทนี้ (และ harness) พึ่งพา จับความผิดพลาดเชิง structure (field required หายไป, JSON type ผิด) ได้ทันทีและฟรี; มันจับ value ที่ผิดเชิงความหมายทุกตัวไม่ได้ (พิสูจน์ด้านบนด้วย
"histogram"ที่ผ่านฉลุย) ดังนั้น API call จริงหรือ--tfTerraform validate ยังเป็นแนวป้องกันสุดท้ายสำหรับ bug ประเภทนั้น
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “cost ของ custom metric scale ตามความถี่ที่ส่ง data point” ผิด — ยกคำพูดตรงจาก billing docs ของ Datadog เองด้านบน: “Your billable usage is not impacted by data point submission frequency.” cost scale ตามจำนวน series ที่แยกกันของ metric-name-บวก-tag-value พิสูจน์ในบทก่อนหน้า
- “Metrics without Limits เปลี่ยนสิ่งที่ application ส่ง” ผิด — พิสูจน์ด้านบนด้วย shape ของ model เอง: config อยู่ทั้งหมดใน resource
metric_tag_configurationแยกต่างหาก key ด้วยชื่อ metric; การ submit ของ DogStatsD/check ของ application ไม่ถูกแตะเลย - “JSON
metric_tag_configurationที่ validate ผ่านสะอาดกับ model จริง รับประกันว่าถูกต้อง” ไม่เชิง — พิสูจน์ด้านบน: client นี้ validate field required กับ type อย่างเข้มงวด แต่ value ของmetric_typeอย่าง"histogram"ที่ไม่อยู่ในallowed_valuesจริงยังผ่าน_check_type=Trueโดยไม่ error ช่องว่างจริง document ไว้ที่ README ของ harness คอร์สนี้เองระบุเหตุผลเดียวกันเป๊ะ - “Percentile aggregation พร้อมใช้อัตโนมัติบน DISTRIBUTION metric ไหนก็ได้” ผิด — ตาม docs ที่ยกมาด้านบน: ชุด aggregation default ของ distribution คือ
count/min/max/sum/avg; percentile (p50…p99หรือ custom) เป็น opt-in แยกชัดเจนอีกชั้น