Skip to content
Bluetape4k docs1.11

Recipes and workshops

Latest stable Based on Bluetape4k release 1.11.0

These recipes close ownership, child policy, ordering, capacity, and cleanup inside one scenario rather than listing APIs in isolation.

suspend fun dashboard(userId: String): Dashboard = coroutineScope {
val profile = async { profileClient.load(userId) }
val alerts = async { alertClient.load(userId) }
Dashboard(profile.await(), alerts.await())
}
  • owner: request caller;
  • failure: atomic result, both dependencies required;
  • cleanup: caller cancellation reaches both children;
  • signals: dependency latency and total request latency.

Do not move these children into a separate DefaultCoroutineScope.

Recipe 2: fastest completion versus first success

Section titled “Recipe 2: fastest completion versus first success”
suspend fun fastestCache(key: String): Value = coroutineScope {
listOf(
async { localCache.get(key) },
async { regionalCache.get(key) },
).awaitAnyAndCancelOthers()
}
suspend fun firstAvailable(key: String): Value = firstSuccessTaskScope {
fork { primary.get(key) }
fork { fallback.get(key) }
join().result { cause -> ValueUnavailable(key, cause) }
}

The first function accepts a fast failure as winner. The second skips failures until success. Idempotency is a prerequisite for racing side effects.

Recipe 3: ordered response versus throughput work

Section titled “Recipe 3: ordered response versus throughput work”
val response = ids.asFlow()
.async(Dispatchers.IO) { catalog.load(it) }
.toList() // input order
events.asFlow()
.mapParallel(parallelism = databasePoolSize / 2) { repository.store(it) }
.collect() // completion order may differ

Do not force public response ordering and background-work capacity through the same operator.

coroutineScope {
val subject = PublishSubject<Event>()
val collector = launch { subject.collect(handler) }
subject.awaitCollector()
val registration = source.register(
onEvent = { event -> launch { subject.emit(event) } },
onError = { error -> launch { subject.emitError(error) } },
)
try {
collector.join()
} finally {
registration.close()
collector.cancelAndJoin()
}
}

Adapt callback-thread scheduling and ownership to the real integration. The invariants are registration before first emission and source cleanup in finally.

Learning goalWorkshopManual chapter
parallel Flow enrichmentkotlin/flow-extensions-parallel-enrichmentFlow
race versus fallbackkotlin/flow-extensions-race-fallbackDeferred
Subject delivery comparisonkotlin/flow-extensions-subject-bridgeSubjects
web request lifecyclespring-boot/webflux-coroutinesLifecycle
trace and metric propagationobservability/micrometer-tracing-coroutinesOperations

Workshops are executable companions, not the source of truth. This manual and current library source define API and lifecycle contracts; workshops are where you vary inputs and observe those contracts.

  1. run the happy path and record output order;
  2. make the fastest branch fail;
  3. cancel the collector/caller mid-flight;
  4. exceed downstream capacity with parallelism and buffering;
  5. verify jobs and threads converge to zero after shutdown.

Return to the Coroutine and Flow extensions decision map.