lib/coz/src/point.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const abi = @import("abi.zig");
  3 
  4 pub const ThroughputPoint = struct {
  5     name: []const u8,
  6     counter: abi.Counter = .{},
  7 
  8     pub fn init(name: []const u8) ThroughputPoint {
  9         return .{ .name = name };
 10     }
 11 
 12     pub fn save(self: *const ThroughputPoint) ThroughputSnapshot {
 13         return .{
 14             .origin = self,
 15             .start_count = self.getCount(),
 16         };
 17     }
 18 
 19     pub fn visit(self: *ThroughputPoint, visits: usize) void {
 20         _ = @atomicRmw(usize, &self.counter.count, .Add, visits, .monotonic);
 21     }
 22 
 23     pub fn getCount(self: *const ThroughputPoint) usize {
 24         return @atomicLoad(usize, &self.counter.count, .monotonic);
 25     }
 26 
 27     pub fn counterStruct(self: *ThroughputPoint) *abi.Counter {
 28         return &self.counter;
 29     }
 30 };
 31 
 32 pub const ThroughputSnapshot = struct {
 33     origin: *const ThroughputPoint,
 34     start_count: usize,
 35 
 36     pub fn log(self: ThroughputSnapshot, writer: *std.Io.Writer) !void {
 37         try writer.print("throughput-point\tname={s}\tdelta={d}\n", .{
 38             self.getName(),
 39             self.getDelta(),
 40         });
 41     }
 42 
 43     pub fn getDelta(self: ThroughputSnapshot) usize {
 44         return self.origin.getCount() -% self.start_count;
 45     }
 46 
 47     pub fn getName(self: ThroughputSnapshot) []const u8 {
 48         return self.origin.name;
 49     }
 50 };
 51 
 52 pub const LatencyPoint = struct {
 53     name: []const u8,
 54     begin_counter: abi.Counter = .{},
 55     end_counter: abi.Counter = .{},
 56 
 57     pub fn init(name: []const u8) LatencyPoint {
 58         return .{ .name = name };
 59     }
 60 
 61     pub fn save(self: *const LatencyPoint) LatencySnapshot {
 62         return .{
 63             .origin = self,
 64             .begin_start_count = self.getBeginCount(),
 65             .end_start_count = self.getEndCount(),
 66         };
 67     }
 68 
 69     pub fn visitBegin(self: *LatencyPoint, visits: usize) void {
 70         _ = @atomicRmw(usize, &self.begin_counter.count, .Add, visits, .monotonic);
 71     }
 72 
 73     pub fn visitEnd(self: *LatencyPoint, visits: usize) void {
 74         _ = @atomicRmw(usize, &self.end_counter.count, .Add, visits, .monotonic);
 75     }
 76 
 77     pub fn getBeginCount(self: *const LatencyPoint) usize {
 78         return @atomicLoad(usize, &self.begin_counter.count, .monotonic);
 79     }
 80 
 81     pub fn getEndCount(self: *const LatencyPoint) usize {
 82         return @atomicLoad(usize, &self.end_counter.count, .monotonic);
 83     }
 84 
 85     pub fn beginCounterStruct(self: *LatencyPoint) *abi.Counter {
 86         return &self.begin_counter;
 87     }
 88 
 89     pub fn endCounterStruct(self: *LatencyPoint) *abi.Counter {
 90         return &self.end_counter;
 91     }
 92 };
 93 
 94 pub const LatencySnapshot = struct {
 95     origin: *const LatencyPoint,
 96     begin_start_count: usize,
 97     end_start_count: usize,
 98 
 99     pub fn log(self: LatencySnapshot, writer: *std.Io.Writer) !void {
100         try writer.print("latency-point\tname={s}\tarrivals={d}\tdepartures={d}\tdifference={d}\n", .{
101             self.getName(),
102             self.getBeginDelta(),
103             self.getEndDelta(),
104             self.getDifference(),
105         });
106     }
107 
108     pub fn getBeginDelta(self: LatencySnapshot) usize {
109         return self.origin.getBeginCount() -% self.begin_start_count;
110     }
111 
112     pub fn getEndDelta(self: LatencySnapshot) usize {
113         return self.origin.getEndCount() -% self.end_start_count;
114     }
115 
116     pub fn getDifference(self: LatencySnapshot) usize {
117         return self.origin.getBeginCount() -% self.origin.getEndCount();
118     }
119 
120     pub fn getName(self: LatencySnapshot) []const u8 {
121         return self.origin.name;
122     }
123 };
124 
125 test "throughput point accumulates visits and exposes counter storage" {
126     var point = ThroughputPoint.init("items");
127 
128     point.visit(1);
129     point.visit(4);
130 
131     try std.testing.expectEqual(@as(usize, 5), point.getCount());
132     try std.testing.expectEqualStrings("items", point.name);
133     try std.testing.expectEqual(@as(usize, 0), point.counterStruct().backoff);
134 }
135 
136 test "throughput snapshot reports delta since save" {
137     var point = ThroughputPoint.init("items");
138     point.visit(2);
139     const snapshot = point.save();
140 
141     point.visit(3);
142 
143     try std.testing.expectEqual(@as(usize, 3), snapshot.getDelta());
144     try std.testing.expectEqualStrings("items", snapshot.getName());
145 }
146 
147 test "throughput snapshot log matches upstream format" {
148     var point = ThroughputPoint.init("items");
149     const snapshot = point.save();
150     point.visit(7);
151 
152     var buffer: [128]u8 = undefined;
153     var writer = std.Io.Writer.fixed(&buffer);
154     try snapshot.log(&writer);
155 
156     try std.testing.expectEqualStrings("throughput-point\tname=items\tdelta=7\n", writer.buffered());
157 }
158 
159 test "latency point accumulates begin and end visits" {
160     var point = LatencyPoint.init("request");
161 
162     point.visitBegin(5);
163     point.visitEnd(2);
164 
165     try std.testing.expectEqual(@as(usize, 5), point.getBeginCount());
166     try std.testing.expectEqual(@as(usize, 2), point.getEndCount());
167     try std.testing.expectEqual(@as(usize, 0), point.beginCounterStruct().backoff);
168     try std.testing.expectEqual(@as(usize, 0), point.endCounterStruct().backoff);
169 }
170 
171 test "latency snapshot reports arrivals departures and outstanding difference" {
172     var point = LatencyPoint.init("request");
173     point.visitBegin(2);
174     point.visitEnd(1);
175     const snapshot = point.save();
176 
177     point.visitBegin(5);
178     point.visitEnd(3);
179 
180     try std.testing.expectEqual(@as(usize, 5), snapshot.getBeginDelta());
181     try std.testing.expectEqual(@as(usize, 3), snapshot.getEndDelta());
182     try std.testing.expectEqual(@as(usize, 3), snapshot.getDifference());
183     try std.testing.expectEqualStrings("request", snapshot.getName());
184 }
185 
186 test "latency snapshot log matches upstream format" {
187     var point = LatencyPoint.init("request");
188     const snapshot = point.save();
189 
190     point.visitBegin(4);
191     point.visitEnd(1);
192 
193     var buffer: [160]u8 = undefined;
194     var writer = std.Io.Writer.fixed(&buffer);
195     try snapshot.log(&writer);
196 
197     try std.testing.expectEqualStrings(
198         "latency-point\tname=request\tarrivals=4\tdepartures=1\tdifference=3\n",
199         writer.buffered(),
200     );
201 }