or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

concurrent-histograms.mdcore-operations.mddouble-histograms.mdindex.mditerators.mdrecorders.mdserialization.mdspecialized-variants.mdutilities.md

recorders.mddocs/

0

# Recorder Classes

1

2

Recorder classes provide continuous value recording while enabling stable interval histogram snapshots without interrupting active recording operations. They implement a producer-consumer pattern optimal for monitoring and metrics collection scenarios.

3

4

## Recorder

5

6

Core recorder implementation for integer values using writer-reader phasing to coordinate continuous recording with interval reporting.

7

8

```java { .api }

9

public class Recorder implements ValueRecorder, IntervalHistogramProvider<Histogram> {

10

11

// Constructors

12

public Recorder(int numberOfSignificantValueDigits);

13

public Recorder(int numberOfSignificantValueDigits, boolean packed);

14

public Recorder(long highestTrackableValue, int numberOfSignificantValueDigits);

15

public Recorder(long lowestDiscernibleValue,

16

long highestTrackableValue,

17

int numberOfSignificantValueDigits);

18

19

// Recording methods

20

void recordValue(long value);

21

void recordValueWithCount(long value, long count);

22

void recordValueWithExpectedInterval(long value, long expectedInterval);

23

void reset();

24

25

// Interval histogram methods

26

Histogram getIntervalHistogram();

27

Histogram getIntervalHistogram(Histogram histogramToRecycle);

28

void getIntervalHistogramInto(Histogram targetHistogram);

29

}

30

```

31

32

### Core Recording Operations

33

34

**Thread-Safe Recording**: All recording methods are wait-free and thread-safe for concurrent access from multiple threads.

35

36

```java

37

// Create recorder for latency measurements

38

Recorder latencyRecorder = new Recorder(3);

39

40

// Multiple threads can record concurrently

41

// Thread 1 - API requests

42

new Thread(() -> {

43

while (running) {

44

long startTime = System.nanoTime();

45

processApiRequest();

46

long endTime = System.nanoTime();

47

latencyRecorder.recordValue((endTime - startTime) / 1000); // Convert to microseconds

48

}

49

}).start();

50

51

// Thread 2 - Database operations

52

new Thread(() -> {

53

while (running) {

54

long startTime = System.nanoTime();

55

executeDatabaseQuery();

56

long endTime = System.nanoTime();

57

latencyRecorder.recordValue((endTime - startTime) / 1000);

58

}

59

}).start();

60

```

61

62

### Interval Histogram Extraction

63

64

The key feature of recorders is non-blocking interval histogram extraction:

65

66

```java

67

// Recording continues uninterrupted while extracting intervals

68

ScheduledExecutorService reporter = Executors.newSingleThreadScheduledExecutor();

69

70

reporter.scheduleAtFixedRate(() -> {

71

// Get interval histogram (automatically resets internal state)

72

Histogram intervalHist = latencyRecorder.getIntervalHistogram();

73

74

if (intervalHist.getTotalCount() > 0) {

75

// Analyze this interval's measurements

76

double mean = intervalHist.getMean();

77

long p95 = intervalHist.getValueAtPercentile(95.0);

78

long p99 = intervalHist.getValueAtPercentile(99.0);

79

80

System.out.printf("Interval: count=%d, mean=%.1fμs, P95=%dμs, P99=%dμs%n",

81

intervalHist.getTotalCount(), mean, p95, p99);

82

83

// Send to monitoring system

84

sendToMonitoring("latency.count", intervalHist.getTotalCount());

85

sendToMonitoring("latency.mean", mean);

86

sendToMonitoring("latency.p95", p95);

87

sendToMonitoring("latency.p99", p99);

88

}

89

}, 0, 10, TimeUnit.SECONDS);

90

```

91

92

### Memory-Efficient Interval Extraction

93

94

Recorders support histogram recycling to reduce garbage collection:

95

96

```java

97

Recorder recorder = new Recorder(3);

98

99

// Reuse histogram objects to minimize GC pressure

100

Histogram recycledHistogram = null;

101

102

while (true) {

103

Thread.sleep(5000); // 5-second intervals

104

105

// Reuse previous histogram object

106

Histogram intervalHist = recorder.getIntervalHistogram(recycledHistogram);

107

108

if (intervalHist.getTotalCount() > 0) {

109

processIntervalData(intervalHist);

110

}

111

112

// Keep reference for recycling

113

recycledHistogram = intervalHist;

114

}

115

```

116

117

### Constructor Options

118

119

```java

120

// Basic recorder with auto-resizing

121

Recorder basic = new Recorder(3);

122

123

// Memory-optimized with packed storage

124

Recorder packed = new Recorder(3, true);

125

126

// Fixed range recorder

127

Recorder fixedRange = new Recorder(1_000_000, 3); // Max 1 million

128

129

// Full specification

130

Recorder fullSpec = new Recorder(1, 10_000_000, 4); // Min 1, max 10M, high precision

131

```

132

133

## SingleWriterRecorder

134

135

Optimized recorder for single-writer scenarios providing better performance when only one thread records values.

136

137

```java { .api }

138

public class SingleWriterRecorder extends Recorder {

139

140

// Constructors

141

public SingleWriterRecorder(int numberOfSignificantValueDigits);

142

public SingleWriterRecorder(int numberOfSignificantValueDigits, boolean packed);

143

public SingleWriterRecorder(long highestTrackableValue, int numberOfSignificantValueDigits);

144

public SingleWriterRecorder(long lowestDiscernibleValue,

145

long highestTrackableValue,

146

int numberOfSignificantValueDigits);

147

}

148

```

149

150

### Usage Patterns

151

152

**Single-Threaded Recording**: Optimized for scenarios where only one thread performs measurements.

153

154

```java

155

// Single-writer recorder for main application thread measurements

156

SingleWriterRecorder mainThreadLatency = new SingleWriterRecorder(3);

157

158

// Main application loop (single thread)

159

public void processRequests() {

160

while (running) {

161

Request request = requestQueue.take();

162

163

long startTime = System.nanoTime();

164

processRequest(request);

165

long endTime = System.nanoTime();

166

167

// Only this thread records (optimized path)

168

mainThreadLatency.recordValue((endTime - startTime) / 1000);

169

}

170

}

171

172

// Separate monitoring thread extracts intervals

173

ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();

174

monitor.scheduleAtFixedRate(() -> {

175

Histogram interval = mainThreadLatency.getIntervalHistogram();

176

analyzeMainThreadPerformance(interval);

177

}, 0, 30, TimeUnit.SECONDS);

178

```

179

180

### Performance Benefits

181

182

SingleWriterRecorder provides:

183

- **Lower overhead** for recording operations (no inter-thread coordination)

184

- **Better cache locality** (single writer, single reader pattern)

185

- **Reduced contention** (no atomic operations between recording threads)

186

187

## DoubleRecorder

188

189

Recorder implementation for double (floating-point) values.

190

191

```java { .api }

192

public class DoubleRecorder implements DoubleValueRecorder, IntervalHistogramProvider<DoubleHistogram> {

193

194

// Constructors

195

public DoubleRecorder(int numberOfSignificantValueDigits);

196

public DoubleRecorder(long highestToLowestValueRatio, int numberOfSignificantValueDigits);

197

198

// Recording methods

199

void recordValue(double value);

200

void recordValueWithCount(double value, long count);

201

void recordValueWithExpectedInterval(double value, double expectedInterval);

202

void reset();

203

204

// Interval histogram methods

205

DoubleHistogram getIntervalHistogram();

206

DoubleHistogram getIntervalHistogram(DoubleHistogram histogramToRecycle);

207

void getIntervalHistogramInto(DoubleHistogram targetHistogram);

208

}

209

```

210

211

### Usage Examples

212

213

```java

214

// Create double recorder for response time measurements (in seconds)

215

DoubleRecorder responseTimeRecorder = new DoubleRecorder(3);

216

217

// Record response times from multiple services

218

public void recordServiceCall(String service, Supplier<String> serviceCall) {

219

double startTime = System.nanoTime() / 1e9;

220

221

try {

222

String result = serviceCall.get();

223

} finally {

224

double endTime = System.nanoTime() / 1e9;

225

double responseTime = endTime - startTime;

226

227

responseTimeRecorder.recordValue(responseTime);

228

}

229

}

230

231

// Extract interval measurements

232

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

233

scheduler.scheduleAtFixedRate(() -> {

234

DoubleHistogram interval = responseTimeRecorder.getIntervalHistogram();

235

236

if (interval.getTotalCount() > 0) {

237

double meanSeconds = interval.getMean();

238

double p95Seconds = interval.getValueAtPercentile(95.0);

239

double p99Seconds = interval.getValueAtPercentile(99.0);

240

241

System.out.printf("Response times - Mean: %.3fs, P95: %.3fs, P99: %.3fs%n",

242

meanSeconds, p95Seconds, p99Seconds);

243

244

// Check SLA compliance

245

if (p95Seconds > 2.0) { // P95 should be under 2 seconds

246

alertingService.sendAlert("P95 response time exceeded SLA: " + p95Seconds + "s");

247

}

248

}

249

}, 0, 60, TimeUnit.SECONDS);

250

```

251

252

### Coordinated Omission Correction

253

254

DoubleRecorder supports coordinated omission correction for timing measurements:

255

256

```java

257

DoubleRecorder timerRecorder = new DoubleRecorder(3);

258

259

// Expected measurement interval

260

double expectedInterval = 0.100; // 100ms expected intervals

261

262

public void performPeriodicMeasurement() {

263

while (running) {

264

double startTime = System.nanoTime() / 1e9;

265

266

// Measurement that might be delayed by system pauses

267

double measurement = performActualMeasurement();

268

269

// Record with coordinated omission correction

270

timerRecorder.recordValueWithExpectedInterval(measurement, expectedInterval);

271

272

try {

273

Thread.sleep(100); // Try to maintain 100ms intervals

274

} catch (InterruptedException e) {

275

break;

276

}

277

}

278

}

279

```

280

281

## SingleWriterDoubleRecorder

282

283

Single-writer optimized version of DoubleRecorder.

284

285

```java { .api }

286

public class SingleWriterDoubleRecorder extends DoubleRecorder {

287

288

// Constructors

289

public SingleWriterDoubleRecorder(int numberOfSignificantValueDigits);

290

public SingleWriterDoubleRecorder(long highestToLowestValueRatio, int numberOfSignificantValueDigits);

291

}

292

```

293

294

### Usage Examples

295

296

```java

297

// Single-writer double recorder for precise timing measurements

298

SingleWriterDoubleRecorder precisionTimer = new SingleWriterDoubleRecorder(4); // High precision

299

300

// Measurement loop (single thread only)

301

public void performPrecisionMeasurements() {

302

while (running) {

303

double startTime = System.nanoTime() / 1e9;

304

305

// Critical operation requiring precise timing

306

performCriticalOperation();

307

308

double endTime = System.nanoTime() / 1e9;

309

double duration = endTime - startTime;

310

311

// Single-threaded optimized recording

312

precisionTimer.recordValue(duration);

313

314

// Maintain measurement cadence

315

maintainCadence();

316

}

317

}

318

```

319

320

## IntervalHistogramProvider Interface

321

322

Common interface for classes providing interval histogram snapshots.

323

324

```java { .api }

325

public interface IntervalHistogramProvider<T extends EncodableHistogram> {

326

T getIntervalHistogram();

327

T getIntervalHistogram(T histogramToRecycle);

328

void getIntervalHistogramInto(T targetHistogram);

329

}

330

```

331

332

## Advanced Recording Patterns

333

334

### Multi-Metric Recording System

335

336

```java

337

public class MetricsRecordingSystem {

338

private final Recorder requestLatency = new Recorder(3);

339

private final Recorder requestSize = new Recorder(2); // Lower precision for size

340

private final DoubleRecorder errorRate = new DoubleRecorder(2);

341

342

public void recordRequest(long latencyMicros, long sizeBytes, boolean success) {

343

requestLatency.recordValue(latencyMicros);

344

requestSize.recordValue(sizeBytes);

345

errorRate.recordValue(success ? 0.0 : 1.0); // 0 for success, 1 for error

346

}

347

348

public void generateIntervalReport() {

349

Histogram latencyHist = requestLatency.getIntervalHistogram();

350

Histogram sizeHist = requestSize.getIntervalHistogram();

351

DoubleHistogram errorHist = errorRate.getIntervalHistogram();

352

353

if (latencyHist.getTotalCount() > 0) {

354

long totalRequests = latencyHist.getTotalCount();

355

double errorRate = errorHist.getMean(); // Mean of 0s and 1s = error rate

356

357

System.out.printf("Interval Report:%n");

358

System.out.printf(" Total Requests: %d%n", totalRequests);

359

System.out.printf(" Error Rate: %.2f%%%n", errorRate * 100);

360

System.out.printf(" Latency P95: %d μs%n", latencyHist.getValueAtPercentile(95.0));

361

System.out.printf(" Size P95: %d bytes%n", sizeHist.getValueAtPercentile(95.0));

362

}

363

}

364

}

365

```

366

367

### Hierarchical Metrics with Tags

368

369

```java

370

public class ServiceMetrics {

371

private final Map<String, Recorder> serviceRecorders = new ConcurrentHashMap<>();

372

373

public void recordServiceCall(String serviceName, long latencyMicros) {

374

Recorder recorder = serviceRecorders.computeIfAbsent(serviceName,

375

name -> {

376

Recorder r = new Recorder(3);

377

r.setTag("service:" + name);

378

return r;

379

});

380

recorder.recordValue(latencyMicros);

381

}

382

383

public void generateServiceReports() {

384

serviceRecorders.forEach((serviceName, recorder) -> {

385

Histogram interval = recorder.getIntervalHistogram();

386

387

if (interval.getTotalCount() > 0) {

388

System.out.printf("Service %s:%n", serviceName);

389

System.out.printf(" Calls: %d%n", interval.getTotalCount());

390

System.out.printf(" Mean: %.1f μs%n", interval.getMean());

391

System.out.printf(" P99: %d μs%n", interval.getValueAtPercentile(99.0));

392

}

393

});

394

}

395

}

396

```

397

398

### Coordinated Recording with Reset Synchronization

399

400

```java

401

public class CoordinatedMetricsSystem {

402

private final List<Recorder> allRecorders = new ArrayList<>();

403

private final Object resetLock = new Object();

404

405

public Recorder createRecorder(String name, int precision) {

406

Recorder recorder = new Recorder(precision);

407

recorder.setTag(name);

408

409

synchronized (resetLock) {

410

allRecorders.add(recorder);

411

}

412

413

return recorder;

414

}

415

416

public Map<String, Histogram> getCoordinatedIntervals() {

417

Map<String, Histogram> intervals = new HashMap<>();

418

419

synchronized (resetLock) {

420

// Get all intervals atomically

421

for (Recorder recorder : allRecorders) {

422

String tag = recorder.getTag();

423

Histogram interval = recorder.getIntervalHistogram();

424

intervals.put(tag, interval);

425

}

426

}

427

428

return intervals;

429

}

430

}

431

```

432

433

## Performance Considerations

434

435

### Recording Performance

436

- **Recorder**: Excellent multi-threaded recording performance

437

- **SingleWriterRecorder**: Best single-threaded performance

438

- **DoubleRecorder**: Slight overhead for double-to-internal conversion

439

- **SingleWriterDoubleRecorder**: Optimized single-threaded double recording

440

441

### Memory Efficiency

442

- Use histogram recycling with `getIntervalHistogram(histogramToRecycle)`

443

- Consider packed storage for sparse distributions

444

- Monitor GC impact of frequent interval extraction

445

446

### Thread Safety

447

All recorder classes provide:

448

- **Wait-free recording** from multiple threads

449

- **Non-blocking interval extraction**

450

- **Proper memory visibility** guarantees

451

- **Coordinated reset** behavior

452

453

## Common Use Cases

454

455

### Application Performance Monitoring (APM)

456

457

```java

458

// APM system using recorders

459

public class APMSystem {

460

private final Recorder httpRequestLatency = new Recorder(3);

461

private final Recorder databaseQueryLatency = new Recorder(3);

462

private final DoubleRecorder cpuUtilization = new DoubleRecorder(2);

463

464

// Called by HTTP request interceptors

465

public void recordHttpRequest(long latencyMicros) {

466

httpRequestLatency.recordValue(latencyMicros);

467

}

468

469

// Called by database interceptors

470

public void recordDatabaseQuery(long latencyMicros) {

471

databaseQueryLatency.recordValue(latencyMicros);

472

}

473

474

// Called by system monitor

475

public void recordCpuUtilization(double percentage) {

476

cpuUtilization.recordValue(percentage / 100.0); // Store as 0-1 ratio

477

}

478

479

@Scheduled(fixedRate = 30000) // Every 30 seconds

480

public void publishMetrics() {

481

publishHistogram("http.latency", httpRequestLatency.getIntervalHistogram());

482

publishHistogram("db.latency", databaseQueryLatency.getIntervalHistogram());

483

publishHistogram("cpu.utilization", cpuUtilization.getIntervalHistogram());

484

}

485

}

486

```

487

488

### Load Testing Framework

489

490

```java

491

// Load testing with detailed latency recording

492

public class LoadTester {

493

private final SingleWriterRecorder latencyRecorder = new SingleWriterRecorder(3);

494

private volatile boolean testRunning = true;

495

496

public void runLoadTest(int durationSeconds, int requestsPerSecond) {

497

// Start metrics collection

498

ScheduledExecutorService metricsCollector = Executors.newSingleThreadScheduledExecutor();

499

metricsCollector.scheduleAtFixedRate(this::collectMetrics, 0, 1, TimeUnit.SECONDS);

500

501

// Run load test

502

long testEndTime = System.currentTimeMillis() + durationSeconds * 1000L;

503

504

while (System.currentTimeMillis() < testEndTime) {

505

long startTime = System.nanoTime();

506

507

try {

508

makeTestRequest();

509

} catch (Exception e) {

510

// Record failed requests with high latency value

511

latencyRecorder.recordValue(Long.MAX_VALUE / 1000); // Error marker

512

continue;

513

}

514

515

long endTime = System.nanoTime();

516

latencyRecorder.recordValue((endTime - startTime) / 1000); // Microseconds

517

518

// Rate limiting

519

Thread.sleep(1000 / requestsPerSecond);

520

}

521

522

testRunning = false;

523

metricsCollector.shutdown();

524

525

// Final report

526

generateFinalReport();

527

}

528

529

private void collectMetrics() {

530

if (!testRunning) return;

531

532

Histogram interval = latencyRecorder.getIntervalHistogram();

533

if (interval.getTotalCount() > 0) {

534

System.out.printf("RPS: %d, P50: %d μs, P95: %d μs, P99: %d μs%n",

535

interval.getTotalCount(),

536

interval.getValueAtPercentile(50.0),

537

interval.getValueAtPercentile(95.0),

538

interval.getValueAtPercentile(99.0));

539

}

540

}

541

}

542

```