Skip to content

Commit db03831

Browse files
authored
Merge pull request #57 from cacheMon/claude/keen-volta-u9ls6v
Clarify that network traces are compressed (.csv.zst) + add regression tests
2 parents 0128dfe + b419f39 commit db03831

2 files changed

Lines changed: 78 additions & 4 deletions

File tree

docs/traces/NETWORK_EVENTS.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,18 @@ from the kernel `bpf_ktime_get_ns()`) — the common cross-stream correlation
2828
clock. Add the manifest's `clock.mono_to_real_offset_ns` to recover wall-clock
2929
nanoseconds.
3030

31-
## Connection Lifecycle — `nw_conn/nw_conn_*.csv`
31+
**Output Files:** Like every other trace stream, network streams are
32+
Zstandard-compressed on disk and on upload:
33+
34+
- `linux_trace_v4_test/{MACHINE_ID}/{TIMESTAMP}/nw_conn/nw_conn_*.csv.zst`
35+
- `linux_trace_v4_test/{MACHINE_ID}/{TIMESTAMP}/nw_epoll/nw_epoll_*.csv.zst`
36+
- `linux_trace_v4_test/{MACHINE_ID}/{TIMESTAMP}/nw_sockopt/nw_sockopt_*.csv.zst`
37+
- `linux_trace_v4_test/{MACHINE_ID}/{TIMESTAMP}/nw_drop/nw_drop_*.csv.zst`
38+
39+
(They are left uncompressed only when the optional `zstandard` library is
40+
unavailable — the same fallback that applies to all streams.)
41+
42+
## Connection Lifecycle — `nw_conn/nw_conn_*.csv.zst`
3243

3344
| # | Field | Type | Description |
3445
|---|-------|------|-------------|
@@ -51,7 +62,7 @@ nanoseconds.
5162
| 17 | return_value | `s32` | Syscall return value |
5263
| 18 | mono_ns | `u64` | Cross-stream correlation clock |
5364

54-
## Multiplexing (epoll/poll/select) — `nw_epoll/nw_epoll_*.csv`
65+
## Multiplexing (epoll/poll/select) — `nw_epoll/nw_epoll_*.csv.zst`
5566

5667
| # | Field | Type | Description |
5768
|---|-------|------|-------------|
@@ -70,7 +81,7 @@ nanoseconds.
7081
| 13 | latency_ns | `u64` | Wait entry→exit latency; empty otherwise |
7182
| 14 | mono_ns | `u64` | Cross-stream correlation clock |
7283

73-
## Socket Options — `nw_sockopt/nw_sockopt_*.csv`
84+
## Socket Options — `nw_sockopt/nw_sockopt_*.csv.zst`
7485

7586
| # | Field | Type | Description |
7687
|---|-------|------|-------------|
@@ -85,7 +96,7 @@ nanoseconds.
8596
| 9 | return_value | `s32` | Syscall return value |
8697
| 10 | mono_ns | `u64` | Cross-stream correlation clock |
8798

88-
## Drops & Retransmits — `nw_drop/nw_drop_*.csv`
99+
## Drops & Retransmits — `nw_drop/nw_drop_*.csv.zst`
89100

90101
| # | Field | Type | Description |
91102
|---|-------|------|-------------|

tests/test_writer_upload.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,5 +345,68 @@ def test_only_first_part_has_header(self):
345345
self.assertEqual(second[0], "rowB,2")
346346

347347

348+
class NetworkStreamCompressionTests(unittest.TestCase):
349+
"""Network streams (nw_conn/nw_epoll/nw_sockopt/nw_drop) must be compressed
350+
and uploaded exactly like every other trace stream — both at shutdown
351+
(force_flush) and on mid-trace rotation."""
352+
353+
def setUp(self):
354+
self.tmp = tempfile.mkdtemp()
355+
self.output_dir = os.path.join(self.tmp, "trace")
356+
self.upload = FakeUploadManager()
357+
self.wm = SilentWriteManager(
358+
output_dir=self.output_dir,
359+
upload_manager=self.upload,
360+
automatic_upload=True,
361+
)
362+
363+
def tearDown(self):
364+
import shutil
365+
try:
366+
self.wm.close_handles()
367+
except Exception:
368+
pass
369+
shutil.rmtree(self.tmp, ignore_errors=True)
370+
371+
def test_all_network_streams_registered_for_rotation(self):
372+
# Every network stream must be in the generic rotation registry so a
373+
# slow stream is compressed+uploaded mid-trace, not just at shutdown.
374+
for key in ("nw_conn", "nw_epoll", "nw_sockopt", "nw_drop"):
375+
self.assertIn(key, self.wm._streams, key)
376+
377+
@unittest.skipUnless(HAS_ZSTD, "zstandard not installed")
378+
def test_force_flush_compresses_network_streams(self):
379+
self.wm.append_conn_log("ts,CONNECT,1,1,proc,AF_INET")
380+
self.wm.append_epoll_log("ts,EPOLL_WAIT,1,1,proc")
381+
self.wm.append_sockopt_log("ts,SET,1,proc,3")
382+
self.wm.append_drop_log("ts,PACKET_DROP,1,proc,TCP")
383+
384+
self.wm.force_flush()
385+
386+
# Each stream produced exactly one compressed upload under its own subdir.
387+
self.assertEqual(len(self.upload.uploaded), 4)
388+
subdirs = {os.path.basename(os.path.dirname(p)) for p in self.upload.uploaded}
389+
for sub in ("nw_conn", "nw_epoll", "nw_sockopt", "nw_drop"):
390+
self.assertIn(sub, subdirs, sub)
391+
for p in self.upload.uploaded:
392+
self.assertTrue(p.endswith(".csv.zst"), p)
393+
self.assertTrue(os.path.exists(p))
394+
395+
@unittest.skipUnless(HAS_ZSTD, "zstandard not installed")
396+
def test_network_threshold_rotation_compresses(self):
397+
# Dropping the threshold forces a mid-trace rotation, which must
398+
# compress+upload the rotated file just like the continuous streams.
399+
self.wm.nw_conn_max_events = 3
400+
for i in range(7):
401+
self.wm.append_conn_log(f"row{i}")
402+
403+
# 7 events at a threshold of 3 → 2 full rotations (2 uploads), with 1
404+
# event left buffered (force_flush is not called here).
405+
self.assertEqual(len(self.upload.uploaded), 2)
406+
for p in self.upload.uploaded:
407+
self.assertTrue(p.endswith(".csv.zst"), p)
408+
self.assertEqual(os.path.basename(os.path.dirname(p)), "nw_conn")
409+
410+
348411
if __name__ == "__main__":
349412
unittest.main()

0 commit comments

Comments
 (0)