Skip to content
Bluetape4k docs1.11

Operations and observability

Latest stable Based on Bluetape4k release 1.11.0

Coroutine count alone does not explain behavior. Observe demand, pressure, service time, cancellation reason, and resource ownership at the same boundary.

Minimum operational signals and deterministic shutdown for a coroutine boundary

SignalQuestionUseful dimensions
in-flight jobs/workershow much demand is activeoperation, owner
queue/buffer depthis production faster than downstreamcomponent, capacity
latency P50/P95/P99where is tail latency createdoperation, outcome
cancellationcaller, timeout, or shutdownreason, owner
shutdown durationtime from intake stop to releasecomponent, phase

Do not put request IDs or exception messages in metric labels. Keep metrics bounded and use traces/logs for high-cardinality details.

suspend fun <T> Timer.recordSuspend(block: suspend () -> T): T {
val sample = Timer.start(registry)
try {
return block()
} catch (e: CancellationException) {
cancellationCounter.increment()
throw e
} catch (e: Exception) {
failureCounter.increment()
throw e
} finally {
sample.stop(this)
}
}

Separate caller cancellation from timeout to distinguish abandonment from service latency. Do not turn normal cancellation into an error span, but record its reason and owner.

  • Readiness asks whether new work can be accepted safely.
  • Liveness asks whether the process can recover.

A saturated connection pool or permanently full queue may require throttling intake or dropping readiness. CPU utilization alone is insufficient.

  1. turn readiness off;
  2. stop listeners, consumers, and other intake;
  3. drain bounded work within an explicit timeout;
  4. terminally close channels/subjects, owned scopes, and dispatchers;
  5. record remaining jobs, threads, and resources.
override fun close() = runBlocking {
accepting.set(false)
withTimeoutOrNull(shutdownTimeout) { pendingJobs.joinAll() }
subject.complete()
workerScope.close()
}

Review which thread calls runBlocking and the framework shutdown deadline. Never wait for a dispatcher from work running on that same constrained dispatcher.

SymptomFirst signalNext check
rising latencyqueue wait and downstream latencyparallelism/capacity mismatch
rising timeoutin-flight and retry countretry-times-race amplification
slow shutdownremaining jobs and dispatcher threadsowner/close path
first event missingcollector count and registration timeSubject startup contract
cancellation pollutes errorsexception classificationrethrow CancellationException

Finally, assemble the contracts into runnable scenarios in Recipes and workshops.