Compute Processor Refactoring
./— title: Compute Processor Refactoring Summary author: bsautner date: 2026-01-08 12:15:00 -04:00 description: Refactoring of compute processor architecture to follow node processor patterns, removing platform-specific dependencies and moving logic to commonMain for multiplatform support product: Krill Server categories: [Krill Server, Internals, Refactoring] tags: [refactoring, compute, multiplatform, processors, architecture] render_with_liquid: false layout: post pin: false mermaid: false —
Compute Processor Refactoring Summary
Overview
Refactored the compute processing architecture to follow the same patterns as ServerCalculationProcessor and ServerCronProcessor, removing java.util dependencies and moving logic to commonMain for multiplatform support.
Changes Made
1. Refactored CronLogic (krill-sdk/src/commonMain/kotlin/krill/zone/krillapp/trigger/cron/CronLogic.kt)
- Removed: Platform-specific actual/expect pattern with java.time dependencies
- Added: Single implementation in commonMain using kotlinx-datetime
- Changed: Clock usage to kotlin.time.Clock.System.now()
- Benefits:
- No more platform-specific implementations needed
- Fully multiplatform compatible
- Simplified maintenance
Deleted Files:
krill-sdk/src/jvmMain/java/krill/zone/krillapp/trigger/cron/CronLogic.jvm.ktkrill-sdk/src/jvmMain/java/krill/zone/krillapp/trigger/cron/ServerCronLogic.ktkrill-sdk/src/androidMain/kotlin/krill/zone/krillapp/trigger/cron/CronLogic.android.ktkrill-sdk/src/iosMain/kotlin/krill/zone/krillapp/trigger/cron/CronLogic.ios.ktkrill-sdk/src/wasmJsMain/kotlin/krill/zone/krillapp/trigger/cron/CronLogic.wasmJs.kt
2. Created ComputeLogic (krill-sdk/src/commonMain/kotlin/krill/zone/krillapp/executor/compute/ComputeLogic.kt)
- Pattern: Same as CronLogic - single implementation in commonMain
- Purpose: Calculate wait time until next interval boundary (minute, hour, day, week, month, year)
- Implementation: Uses kotlinx-datetime for all date/time calculations
- Key Method:
getWaitMillis(node: Node): Long
Interval Types:
- MINUTE: Wait until next minute boundary (e.g., 12:34:00)
- HOUR: Wait until next hour boundary (e.g., 13:00:00)
- DAY: Wait until midnight (00:00:00)
- WEEK: Wait until Sunday at midnight
- MONTH: Wait until 1st of next month at midnight
- YEAR: Wait until January 1st at midnight
3. Refactored ServerComputeProcessor (krill-sdk/src/commonMain/kotlin/krill/zone/krillapp/executor/compute/ServerComputeProcessor.kt)
- Pattern: Follows ServerCronProcessor pattern with continuous loop execution
- Key Features:
- Continuous loop that runs like cron jobs (
keepJobRunning = true) - Waits for interval boundaries using ComputeLogic
- Computes statistics on collected data ranges
- Updates target node with computed snapshots
- Broadcasts events after computation
- Continuous loop that runs like cron jobs (
State Machine:
NONE→ Execute node to startEXECUTED→ Submit continuous loop jobUSER_EDIT→ Cancel and restartDELETING→ Cancel job
Supported Operations:
- AVERAGE/MEAN - Average of values
- MEDIAN - Middle value
- HIGH - Maximum value
- LOW - Minimum value
- SUM - Sum of values
- COUNT - Count of values
Implementation Details
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
override fun post(node: Node) {
super.post(node)
if (!node.isMine()) return
scope.launch {
when (node.state) {
NodeState.EXECUTED -> {
executor.submit(
node = node,
shouldProcess = { it == NodeState.EXECUTED },
executeChildren = true,
keepJobRunning = true // Continuous loop
) { n ->
executeComputeJob(n)
}
}
NodeState.USER_EDIT -> {
executor.cancel(node.id)
nodeManager.execute(node)
}
NodeState.DELETING -> {
executor.cancel(node.id)
}
else -> {}
}
}
}
private suspend fun executeComputeJob(node: Node): Boolean {
while (true) {
// Wait for next interval boundary
val waitMillis = ComputeLogic.getWaitMillis(node)
if (waitMillis > 0) {
delay(waitMillis)
}
// Collect data snapshots from source
val snapshots = collectSnapshots(node)
// Compute statistics
val result = computeStatistics(node, snapshots)
// Update target with result
updateTarget(node, result)
// Broadcast completion event
eventBus.emit(NodeEvent.NodeComputed(node))
}
}
Compute Operations
AVERAGE (MEAN)
1
2
val sum = snapshots.sumOf { it.value.toDouble() }
val average = sum / snapshots.size
MEDIAN
1
2
val sorted = snapshots.sortedBy { it.value.toDouble() }
val median = sorted[sorted.size / 2].value
HIGH (MAX)
1
val max = snapshots.maxByOrNull { it.value.toDouble() }?.value
LOW (MIN)
1
val min = snapshots.minByOrNull { it.value.toDouble() }?.value
SUM
1
val sum = snapshots.sumOf { it.value.toDouble() }
COUNT
1
val count = snapshots.size.toDouble()
Benefits
1. Multiplatform Support
- No platform-specific implementations needed
- Runs on JVM, Android, iOS, WASM, Desktop
- Single source of truth in commonMain
2. Consistency with Other Processors
- Follows same pattern as ServerCronProcessor
- Uses NodeProcessExecutor for job management
- Standardized error handling and state transitions
3. Simplified Maintenance
- One implementation instead of 5 platform-specific files
- Changes apply to all platforms automatically
- Easier to understand and debug
4. Better Testing
- Test once in commonTest
- No need for platform-specific test implementations
- Easier to verify correctness
Migration Notes
For Existing Nodes
- Existing compute nodes continue to work unchanged
- No data migration required
- State transitions preserved
For Developers
- Import from
krill.zone.krillapp.executor.compute.ComputeLogic - Use
ComputeLogic.getWaitMillis(node)for interval calculations - All date/time operations use kotlinx-datetime
Testing
All refactored logic tested and verified:
- ✅ Interval boundary calculations correct
- ✅ Statistics computations accurate
- ✅ Continuous loop execution working
- ✅ Error handling proper
- ✅ State transitions correct
- ✅ Event broadcasting functional
Performance Considerations
Memory Usage
- Collects snapshots for entire interval period
- May accumulate large datasets for long intervals
- Consider adding max snapshot limit
CPU Usage
- Minimal - only computes at interval boundaries
- Efficient with delay() instead of polling
- No unnecessary wakeups
Network Usage
- Minimal - only updates target on interval boundaries
- Efficient batching of computations
- No redundant data transfers
Future Enhancements
- Snapshot Limit: Add configurable max snapshots to prevent memory issues
- Rolling Windows: Support sliding window calculations
- More Operations: Add std deviation, variance, percentiles
- Partial Intervals: Handle incomplete interval periods gracefully
- Custom Intervals: Allow arbitrary time periods beyond fixed boundaries
Related Documents
Conclusion
The compute processor refactoring aligns it with the established patterns used throughout the codebase, making it easier to maintain and extend while providing full multiplatform support. The removal of platform-specific code significantly reduces complexity and testing burden.