This is an automated email from the ASF dual-hosted git repository.
hello-stephen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 317ca22ce77 [improvement](ci) Compress Litefuse review uploads with
gzip (#69008)
317ca22ce77 is described below
commit 317ca22ce77b44c8ceff3a42cf357fc380bdfa91
Author: shuke <[email protected]>
AuthorDate: Tue Oct 13 16:11:04 2026 +0800
[improvement](ci) Compress Litefuse review uploads with gzip (#69008)
Problem Summary:
Code Review Runner currently sends uncompressed OTLP JSON to Litefuse.
Large reviews can upload tens of megabytes: run 38323941693 sent
61,506,031 bytes across 26 successful POSTs. Enable gzip for the
runner's main and subagent uploads to reduce transferred bytes.
The exporter adds an opt-in `--gzip` flag using gzip level 1. Chunk
limits and HTTP 413 splitting continue to use uncompressed sizes.
Existing retry behavior and rejection checks, including HTTP 200
responses with rejected spans, are preserved. Per-trace results now
report compression, successful request body sizes on the wire, and
upload duration (including chunk preparation and retries, excluding
readback).
Litefuse remains optional telemetry: its workflow step retains
`continue-on-error: true`. Upload or readback errors are still surfaced
in logs and do not determine the review result or aggregate gate.
Uploads remain serial, and readback settings and timeouts are unchanged.
Validation:
- 40 exporter unit tests pass, including gzip round trips with Unicode
IO, uncompressed chunk limits, main/subagent readback, transport
retries, HTTP 413 splitting/shrinking, partial rejection, and HTTP
415/429/503 failures.
- `actionlint -shellcheck= -pyflakes=` passes for the runner workflow;
no whitespace errors.
- Live compatibility check against the configured Litefuse test project:
one explicitly synthetic gzip trace returned HTTP 200 and its complete
input/output matched on readback. Request body: 1,543 bytes before
compression, 576 bytes on the wire. This verifies compatibility, not
production speedup; the added metrics support measurement after rollout.
---
.github/scripts/emit_litefuse_otel_io.py | 30 ++++++-
.github/scripts/test_emit_litefuse_otel_io.py | 108 +++++++++++++++++++++++++-
.github/workflows/code-review-runner.yml | 2 +
3 files changed, 133 insertions(+), 7 deletions(-)
diff --git a/.github/scripts/emit_litefuse_otel_io.py
b/.github/scripts/emit_litefuse_otel_io.py
index 3c6cf3846cb..5f6a44817dc 100644
--- a/.github/scripts/emit_litefuse_otel_io.py
+++ b/.github/scripts/emit_litefuse_otel_io.py
@@ -18,6 +18,7 @@
import argparse
import base64
+import gzip
from datetime import datetime, timedelta, timezone
import hashlib
import json
@@ -1458,11 +1459,16 @@ def shrink_singleton_otlp_retry(
return otlp_chunks(chunk, next_limit, trace_body)
-def post_payload_once(endpoint, public_key, secret_key, payload,
timeout_seconds):
+def post_payload_once(
+ endpoint, public_key, secret_key, payload, timeout_seconds,
gzip_enabled=False
+):
auth = base64.b64encode(f"{public_key}:{secret_key}".encode()).decode()
+ data = compact_json_bytes(payload)
+ if gzip_enabled:
+ data = gzip.compress(data, compresslevel=1, mtime=0)
request = urllib.request.Request(
endpoint,
- data=compact_json_bytes(payload),
+ data=data,
headers={
"Content-Type": "application/json",
"Authorization": f"Basic {auth}",
@@ -1472,6 +1478,8 @@ def post_payload_once(endpoint, public_key, secret_key,
payload, timeout_seconds
},
method="POST",
)
+ if gzip_enabled:
+ request.add_header("Content-Encoding", "gzip")
with urllib.request.urlopen(request, timeout=timeout_seconds) as response:
body = response.read().decode()
detail = json.loads(body) if body else {}
@@ -1493,6 +1501,7 @@ def post_payload_once(endpoint, public_key, secret_key,
payload, timeout_seconds
return {
"status": response.status,
"success_count": otlp_span_count(payload),
+ "wire_bytes": len(data),
}
@@ -1505,10 +1514,13 @@ def post_payload(
timeout_seconds,
retry_attempts,
retry_sleep_seconds,
+ gzip_enabled=False,
):
+ upload_started = time.monotonic()
statuses = []
success_count = 0
request_sizes = []
+ wire_request_sizes = []
payload_too_large_retry_count = 0
transport_retry_count = 0
http_retry_count = 0
@@ -1522,7 +1534,8 @@ def post_payload(
post_attempt_count += 1
try:
status = post_payload_once(
- endpoint, public_key, secret_key, otlp_chunk, timeout_seconds
+ endpoint, public_key, secret_key, otlp_chunk, timeout_seconds,
+ gzip_enabled=gzip_enabled,
)
except urllib.error.HTTPError as exc:
if exc.code in OTLP_RETRYABLE_HTTP_STATUS_CODES:
@@ -1597,11 +1610,16 @@ def post_payload(
statuses.append(status["status"])
success_count += int(status.get("success_count") or 0)
request_sizes.append(request_size)
+ wire_request_sizes.append(status["wire_bytes"])
return {
+ "compression": "gzip" if gzip_enabled else "none",
+ "upload_seconds": round(time.monotonic() - upload_started, 3),
"statuses": statuses,
"request_count": len(statuses),
"post_attempt_count": post_attempt_count,
"request_sizes": request_sizes,
+ # Like request_sizes, these sizes cover successful requests only.
+ "wire_request_sizes": wire_request_sizes,
"max_request_size": max(request_sizes) if request_sizes else 0,
"payload_too_large_retries": payload_too_large_retry_count,
"transport_retries": transport_retry_count,
@@ -2024,6 +2042,10 @@ def parse_args():
parser.add_argument("--post-timeout-seconds", type=int, default=120)
parser.add_argument("--post-retry-attempts", type=int, default=5)
parser.add_argument("--post-retry-sleep-seconds", type=int, default=5)
+ parser.add_argument(
+ "--gzip", action="store_true",
+ help="Gzip OTLP uploads; payload size limits still apply before
compression",
+ )
parser.add_argument("--verify", action="store_true")
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--verify-attempts", type=int, default=24)
@@ -2113,6 +2135,7 @@ def main():
args.post_timeout_seconds,
args.post_retry_attempts,
args.post_retry_sleep_seconds,
+ gzip_enabled=args.gzip,
)
result["subagent_traces"] = []
for subagent_payload in subagent_payloads:
@@ -2125,6 +2148,7 @@ def main():
args.post_timeout_seconds,
args.post_retry_attempts,
args.post_retry_sleep_seconds,
+ gzip_enabled=args.gzip,
)
result["subagent_traces"].append(
{
diff --git a/.github/scripts/test_emit_litefuse_otel_io.py
b/.github/scripts/test_emit_litefuse_otel_io.py
index 6c7b31960ac..389e42a933b 100644
--- a/.github/scripts/test_emit_litefuse_otel_io.py
+++ b/.github/scripts/test_emit_litefuse_otel_io.py
@@ -18,6 +18,7 @@
import importlib.util
from contextlib import redirect_stdout
+import gzip
import io
import json
from pathlib import Path
@@ -135,8 +136,11 @@ class LitefuseOtelExporterTest(unittest.TestCase):
def reject_payloads_above(self, server_limit, request_sizes):
def fake_urlopen(request, timeout):
- request_sizes.append(len(request.data))
- if len(request.data) > server_limit:
+ data = request.data
+ if request.get_header("Content-encoding") == "gzip":
+ data = gzip.decompress(data)
+ request_sizes.append(len(data))
+ if len(data) > server_limit:
raise urllib.error.HTTPError(
request.full_url,
413,
@@ -450,11 +454,67 @@ class LitefuseOtelExporterTest(unittest.TestCase):
headers = {key.lower(): value for key, value in
captured["request"].header_items()}
self.assertEqual(headers["content-type"], "application/json")
+ self.assertNotIn("content-encoding", headers)
+ self.assertEqual(json.loads(captured["request"].data), payload)
self.assertEqual(headers["x-langfuse-ingestion-version"], "4")
self.assertEqual(headers["x-langfuse-sdk-name"], "doris-code-review")
self.assertEqual(captured["timeout"], 30)
self.assertEqual(status["success_count"], 1)
+ def test_gzip_preserves_io_and_uncompressed_chunk_limits(self):
+ events = [
+ self.span_event(f"{index:032x}", output_size=2_000)
+ for index in range(1, 5)
+ ]
+ for event in events:
+ event["body"]["input"] = {"command": "检查 gzip 上传 🌏"}
+ payload = {"batch": [{"type": "trace-create", "body":
self.trace_body()}, *events]}
+ requests = []
+
+ def fake_urlopen(request, timeout):
+ requests.append(request)
+ return FakeResponse()
+
+ with mock.patch.object(MODULE.urllib.request, "urlopen",
side_effect=fake_urlopen):
+ status = MODULE.post_payload(
+ "https://litefuse.example/api/public/otel/v1/traces",
+ "public", "secret", payload, 8_000, 30, 3, 0,
gzip_enabled=True,
+ )
+
+ self.assertGreater(len(requests), 1)
+ raw_sizes = []
+ spans = []
+ for request in requests:
+ self.assertEqual(request.get_header("Content-encoding"), "gzip")
+ self.assertEqual(request.get_header("Content-type"),
"application/json")
+ raw = gzip.decompress(request.data)
+ raw_sizes.append(len(raw))
+ self.assertLessEqual(len(raw), 8_000)
+ self.assertLess(len(request.data), len(raw))
+ for resource in json.loads(raw)["resourceSpans"]:
+ for scope in resource["scopeSpans"]:
+ spans.extend(scope["spans"])
+ self.assertEqual(len(spans), len(events))
+ self.assertEqual(
+ [span["spanId"] for span in spans],
+ [MODULE.otel_id(event["body"]["id"], 8) for event in events],
+ )
+ for span in spans:
+ attrs = attribute_values(span)
+ self.assertEqual(
+ json.loads(attrs["langfuse.observation.input"]),
+ {"command": "检查 gzip 上传 🌏"},
+ )
+ self.assertEqual(
+ json.loads(attrs["langfuse.observation.output"])["text"], "x"
* 2_000
+ )
+ self.assertEqual(status["compression"], "gzip")
+ self.assertEqual(status["request_sizes"], raw_sizes)
+ self.assertEqual(
+ status["wire_request_sizes"], [len(request.data) for request in
requests]
+ )
+ self.assertGreaterEqual(status["upload_seconds"], 0)
+
def test_paginates_v2_observations_until_root_is_visible(self):
first_page = [{"id": f"child-{index}"} for index in range(1_000)]
responses = [
@@ -516,6 +576,7 @@ class LitefuseOtelExporterTest(unittest.TestCase):
"secret",
payload,
30,
+ gzip_enabled=True,
)
def test_splits_multi_span_chunk_after_transport_error(self):
@@ -546,12 +607,39 @@ class LitefuseOtelExporterTest(unittest.TestCase):
30,
3,
0,
+ gzip_enabled=True,
)
self.assertEqual(status["transport_retries"], 1)
self.assertEqual(status["request_count"], 2)
self.assertEqual(status["success_count"], 2)
+ def test_gzip_upload_failures_are_not_reported_as_success(self):
+ payload = {"batch": [
+ {"type": "trace-create", "body": self.trace_body()},
+ self.span_event("2" * 32),
+ ]}
+ for code, attempts in ((415, 1), (429, 2), (503, 2)):
+ with self.subTest(code=code):
+ errors = [urllib.error.HTTPError(
+ "https://litefuse.example/api/public/otel/v1/traces",
+ code, "ingestion failed", {}, io.BytesIO(b"ingestion
failed"),
+ ) for _ in range(attempts)]
+ with mock.patch.object(
+ MODULE.urllib.request, "urlopen", side_effect=errors
+ ) as send:
+ with self.assertRaisesRegex(RuntimeError, f"HTTP {code}"):
+ MODULE.post_payload(
+
"https://litefuse.example/api/public/otel/v1/traces",
+ "public", "secret", payload, 10_000, 30, 1, 0,
+ gzip_enabled=True,
+ )
+ self.assertEqual(send.call_count, attempts)
+ for call in send.call_args_list:
+ self.assertEqual(
+ call.args[0].get_header("Content-encoding"), "gzip"
+ )
+
def test_splits_multi_span_chunk_after_http_413(self):
trace_event = {"type": "trace-create", "body": self.trace_body()}
payload = {
@@ -583,6 +671,7 @@ class LitefuseOtelExporterTest(unittest.TestCase):
30,
3,
0,
+ gzip_enabled=True,
)
self.assertEqual(status["payload_too_large_retries"], 1)
@@ -615,6 +704,7 @@ class LitefuseOtelExporterTest(unittest.TestCase):
30,
3,
0,
+ gzip_enabled=True,
)
self.assertEqual(status["payload_too_large_retries"], 1)
@@ -652,6 +742,7 @@ class LitefuseOtelExporterTest(unittest.TestCase):
30,
5,
0,
+ gzip_enabled=True,
)
self.assertEqual(initial_size, 9_486)
@@ -687,6 +778,7 @@ class LitefuseOtelExporterTest(unittest.TestCase):
30,
3,
0,
+ gzip_enabled=True,
)
self.assertEqual(len(request_sizes), 4)
@@ -757,6 +849,7 @@ class LitefuseOtelExporterTest(unittest.TestCase):
30,
3,
0,
+ gzip_enabled=True,
)
self.assertEqual(status["http_retries"], 1)
@@ -975,7 +1068,7 @@ class LitefuseOtelExporterTest(unittest.TestCase):
self.assertEqual(timeouts, [5, 2])
def
test_main_exports_and_verifies_all_traces_with_failure_diagnostics(self):
- for mode in ("success", "missing_subagent", "main_only", "no_verify",
"dry_run"):
+ for mode in ("success", "gzip", "missing_subagent", "main_only",
"no_verify", "dry_run"):
with self.subTest(mode=mode):
argv = [
"exporter", "--input-file", "prompt", "--events-file",
"events",
@@ -988,6 +1081,8 @@ class LitefuseOtelExporterTest(unittest.TestCase):
argv.append("--verify")
if mode == "dry_run":
argv.append("--dry-run")
+ if mode == "gzip":
+ argv.append("--gzip")
with mock.patch.object(sys, "argv", argv):
args = MODULE.parse_args()
events = [
@@ -1011,7 +1106,12 @@ class LitefuseOtelExporterTest(unittest.TestCase):
def fake_urlopen(request, timeout):
requests.append(request)
if request.method == "POST":
- payload = json.loads(request.data)
+ if mode == "gzip":
+
self.assertEqual(request.get_header("Content-encoding"), "gzip")
+ payload = json.loads(gzip.decompress(request.data))
+ else:
+
self.assertIsNone(request.get_header("Content-encoding"))
+ payload = json.loads(request.data)
for resource in payload["resourceSpans"]:
for scope in resource["scopeSpans"]:
for span in scope["spans"]:
diff --git a/.github/workflows/code-review-runner.yml
b/.github/workflows/code-review-runner.yml
index 23ffcefa2c9..dc8826ac6cc 100644
--- a/.github/workflows/code-review-runner.yml
+++ b/.github/workflows/code-review-runner.yml
@@ -1137,6 +1137,7 @@ jobs:
- name: Record review I/O to Litefuse
if: ${{ always() }}
+ # Telemetry errors must not change the code review result.
continue-on-error: true
timeout-minutes: 5
env:
@@ -1188,6 +1189,7 @@ jobs:
--reasoning-effort "xhigh" \
--environment "github-actions" \
--max-payload-bytes 4000000 \
+ --gzip \
--min-observations "$min_observations" \
--min-step-observations "$min_step_observations" \
--verify-attempts 24 \
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]