Skip to content

Commit 13d1596

Browse files
authored
perf: avoiding unnecessary object creation (#107)
1 parent e51c41a commit 13d1596

File tree

17 files changed

+216
-354
lines changed

17 files changed

+216
-354
lines changed

src/main/kotlin/at/ac/uibk/dps/cirrina/di/CirrinaModule.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package at.ac.uibk.dps.cirrina.cirrina.di
22

33
import at.ac.uibk.dps.cirrina.EnvironmentVariables
44
import at.ac.uibk.dps.cirrina.PersistentContextProvider
5-
import at.ac.uibk.dps.cirrina.execution.`object`.ActionCommandFactory
6-
import at.ac.uibk.dps.cirrina.execution.`object`.ActionCommandFactoryImpl
75
import at.ac.uibk.dps.cirrina.execution.`object`.Context
86
import at.ac.uibk.dps.cirrina.execution.provider.ContextEtcd
97
import at.ac.uibk.dps.cirrina.util.getBuildVersion
@@ -131,11 +129,6 @@ class CirrinaModule {
131129

132130
@Provides @Singleton @Run fun provideRun(): List<String> = EnvironmentVariables.run.get()
133131

134-
@Provides
135-
@Singleton
136-
fun provideActionCommandFactory(meterRegistry: MeterRegistry): ActionCommandFactory =
137-
ActionCommandFactoryImpl(meterRegistry)
138-
139132
private fun createInfluxRegistry(url: String): InfluxMeterRegistry {
140133
val config =
141134
object : InfluxConfig {
Lines changed: 49 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,87 @@
11
package at.ac.uibk.dps.cirrina.execution.`object`
22

33
import at.ac.uibk.dps.cirrina.csm.Csml.EventChannel
4-
import at.ac.uibk.dps.cirrina.execution.service.ServiceImplementation
54
import at.ac.uibk.dps.cirrina.execution.service.ServiceImplementationSelector
6-
import io.micrometer.core.instrument.MeterRegistry
75
import kotlinx.coroutines.CoroutineScope
86
import kotlinx.coroutines.launch
97
import mu.KotlinLogging
108

119
private val logger = KotlinLogging.logger {}
1210

13-
data class CommandExecutionContext(
14-
val scope: Scope,
15-
val serviceImplementationSelector: ServiceImplementationSelector,
16-
val stateMachineEventHandler: StateMachine.StateMachineEventHandler,
17-
val coroutineScope: CoroutineScope,
18-
val raisingEvent: Event? = null,
19-
val isDuring: Boolean = false,
20-
)
21-
22-
interface ActionCommandFactory {
23-
fun create(action: Action, commandExecutionContext: CommandExecutionContext): ActionCommand
24-
}
25-
26-
class ActionCommandFactoryImpl(private val meterRegistry: MeterRegistry) : ActionCommandFactory {
27-
override fun create(
28-
action: Action,
29-
commandExecutionContext: CommandExecutionContext,
30-
): ActionCommand =
31-
when (action) {
32-
is EvalAction -> EvalActionCommand(action, commandExecutionContext, this, meterRegistry)
33-
is InvokeAction -> InvokeActionCommand(action, commandExecutionContext, this, meterRegistry)
34-
is MatchAction -> MatchActionCommand(action, commandExecutionContext, this, meterRegistry)
35-
is EmitAction -> EmitActionCommand(action, commandExecutionContext, this, meterRegistry)
36-
is TimeoutAction -> TimeoutActionCommand(action, commandExecutionContext, this, meterRegistry)
37-
is TimeoutResetAction ->
38-
TimeoutResetActionCommand(action, commandExecutionContext, this, meterRegistry)
39-
is LogAction -> LogActionCommand(action, commandExecutionContext, this, meterRegistry)
40-
else -> error("unknown action type: ${action::class.simpleName}")
41-
}
42-
}
43-
4411
interface Scope {
4512
val extent: Extent
4613
}
4714

48-
abstract class ActionCommand
49-
internal constructor(
50-
protected val commandExecutionContext: CommandExecutionContext,
51-
protected val commandFactory: ActionCommandFactory,
52-
protected val meterRegistry: MeterRegistry,
15+
class ActionExecutor(
16+
private val selector: ServiceImplementationSelector,
17+
private val eventHandler: StateMachine.StateMachineEventHandler,
18+
private val coroutineScope: CoroutineScope,
5319
) {
54-
abstract fun execute(): List<ActionCommand>
55-
}
56-
57-
class EvalActionCommand
58-
internal constructor(
59-
private val evalAction: EvalAction,
60-
commandExecutionContext: CommandExecutionContext,
61-
commandFactory: ActionCommandFactory,
62-
meterRegistry: MeterRegistry,
63-
) : ActionCommand(commandExecutionContext, commandFactory, meterRegistry) {
64-
override fun execute(): List<ActionCommand> =
65-
evalAction.expression.execute(commandExecutionContext.scope.extent).run { emptyList() }
66-
}
67-
68-
class InvokeActionCommand
69-
internal constructor(
70-
private val invokeAction: InvokeAction,
71-
commandExecutionContext: CommandExecutionContext,
72-
commandFactory: ActionCommandFactory,
73-
meterRegistry: MeterRegistry,
74-
) : ActionCommand(commandExecutionContext, commandFactory, meterRegistry) {
75-
private val logger = KotlinLogging.logger {}
76-
77-
override fun execute(): List<ActionCommand> {
78-
val service = selectServiceImplementation()
79-
val input = prepareInput(commandExecutionContext.scope.extent)
80-
81-
commandExecutionContext.coroutineScope.launch {
82-
runCatching { service.invoke(input) }
83-
.onSuccess { emitEvents(it) }
84-
.onFailure { logger.error(it) { "service invocation failed" } }
20+
fun execute(action: Action, scope: Scope): List<Action> =
21+
when (action) {
22+
is EvalAction -> executeEval(action, scope)
23+
is InvokeAction -> executeInvoke(action, scope)
24+
is MatchAction -> executeMatch(action, scope)
25+
is EmitAction -> executeEmit(action, scope)
26+
is TimeoutAction -> listOf(action.triggers)
27+
is TimeoutResetAction -> emptyList()
28+
is LogAction -> executeLog(action, scope)
29+
else -> error("unknown action type: ${action::class.simpleName}")
8530
}
8631

32+
private fun executeEval(action: EvalAction, scope: Scope): List<Action> {
33+
action.expression.execute(scope.extent)
8734
return emptyList()
8835
}
8936

90-
private fun selectServiceImplementation(): ServiceImplementation =
91-
commandExecutionContext.serviceImplementationSelector.select(
92-
invokeAction.type,
93-
invokeAction.mode,
94-
) ?: error("no service implementation found for type '${invokeAction.type}'")
37+
private fun executeInvoke(action: InvokeAction, scope: Scope): List<Action> {
38+
val service =
39+
selector.select(action.type, action.mode)
40+
?: error("no service implementation found for type '${action.type}'")
9541

96-
private fun prepareInput(extent: Extent): List<ContextVariable> =
97-
invokeAction.input.map { it.evaluate(extent) }
42+
val input = action.input.map { it.evaluate(scope.extent) }
9843

99-
private fun emitEvents(output: List<ContextVariable>) {
100-
invokeAction.emits.forEach { eventTemplate ->
101-
val event = eventTemplate.copy(data = output)
102-
val handler =
103-
when (event.channel) {
104-
EventChannel.INTERNAL ->
105-
commandExecutionContext.stateMachineEventHandler::propagateToParent
106-
else -> commandExecutionContext.stateMachineEventHandler::emit
44+
coroutineScope.launch {
45+
runCatching { service.invoke(input) }
46+
.onSuccess { output ->
47+
action.emits.forEach { eventTemplate ->
48+
val emittedEvent = eventTemplate.copy(data = output)
49+
if (emittedEvent.channel == EventChannel.INTERNAL) {
50+
eventHandler.propagateToParent(emittedEvent)
51+
} else {
52+
eventHandler.emit(emittedEvent)
53+
}
54+
}
10755
}
108-
handler(event)
56+
.onFailure { logger.error(it) { "service invocation failed" } }
10957
}
110-
}
111-
}
11258

113-
class MatchActionCommand
114-
internal constructor(
115-
private val matchAction: MatchAction,
116-
commandExecutionContext: CommandExecutionContext,
117-
commandFactory: ActionCommandFactory,
118-
meterRegistry: MeterRegistry,
119-
) : ActionCommand(commandExecutionContext, commandFactory, meterRegistry) {
120-
override fun execute(): List<ActionCommand> {
121-
val extent = commandExecutionContext.scope.extent
122-
123-
val selectedActions: List<Action> =
124-
matchAction.cases.entries
125-
.filter { (expression, _) -> expression.execute(extent) == true }
126-
.flatMap { it.value }
127-
.ifEmpty { listOfNotNull(matchAction.default) }
128-
129-
return selectedActions.map { commandFactory.create(it, commandExecutionContext) }
59+
return emptyList()
13060
}
131-
}
13261

133-
class EmitActionCommand
134-
internal constructor(
135-
private val emitAction: EmitAction,
136-
commandExecutionContext: CommandExecutionContext,
137-
commandFactory: ActionCommandFactory,
138-
meterRegistry: MeterRegistry,
139-
) : ActionCommand(commandExecutionContext, commandFactory, meterRegistry) {
140-
override fun execute(): List<ActionCommand> {
141-
val event =
142-
emitAction.event.evaluateData(commandExecutionContext.scope.extent).run {
143-
val target = emitAction.target?.execute(commandExecutionContext.scope.extent) as? String
62+
private fun executeMatch(action: MatchAction, scope: Scope): List<Action> =
63+
action.cases.entries
64+
.filter { (expression, _) -> expression.execute(scope.extent) == true }
65+
.flatMap { it.value }
66+
.ifEmpty { listOfNotNull(action.default) }
67+
68+
private fun executeEmit(action: EmitAction, scope: Scope): List<Action> {
69+
val emittedEvent =
70+
action.event.evaluateData(scope.extent).run {
71+
val target = action.target?.execute(scope.extent) as? String
14472
if (target != null) copy(target = target) else this
14573
}
14674

147-
with(commandExecutionContext.stateMachineEventHandler) {
148-
if (event.channel == EventChannel.INTERNAL) propagateToParent(event) else emit(event)
75+
with(eventHandler) {
76+
if (emittedEvent.channel == EventChannel.INTERNAL) propagateToParent(emittedEvent)
77+
else emit(emittedEvent)
14978
}
15079

15180
return emptyList()
15281
}
153-
}
154-
155-
class TimeoutActionCommand
156-
internal constructor(
157-
private val timeoutAction: TimeoutAction,
158-
commandExecutionContext: CommandExecutionContext,
159-
commandFactory: ActionCommandFactory,
160-
meterRegistry: MeterRegistry,
161-
) : ActionCommand(commandExecutionContext, commandFactory, meterRegistry) {
162-
override fun execute(): List<ActionCommand> =
163-
listOf(commandFactory.create(timeoutAction.triggers, commandExecutionContext))
164-
}
165-
166-
class TimeoutResetActionCommand
167-
internal constructor(
168-
val timeoutResetAction: TimeoutResetAction,
169-
commandExecutionContext: CommandExecutionContext,
170-
commandFactory: ActionCommandFactory,
171-
meterRegistry: MeterRegistry,
172-
) : ActionCommand(commandExecutionContext, commandFactory, meterRegistry) {
173-
override fun execute(): List<ActionCommand> = emptyList()
174-
}
175-
176-
class LogActionCommand
177-
internal constructor(
178-
private val logAction: LogAction,
179-
commandExecutionContext: CommandExecutionContext,
180-
commandFactory: ActionCommandFactory,
181-
meterRegistry: MeterRegistry,
182-
) : ActionCommand(commandExecutionContext, commandFactory, meterRegistry) {
183-
override fun execute(): List<ActionCommand> {
184-
logAction.message.execute(commandExecutionContext.scope.extent).toString().also {
185-
logger.info(it)
186-
}
18782

83+
private fun executeLog(action: LogAction, scope: Scope): List<Action> {
84+
action.message.execute(scope.extent).toString().also { logger.info(it) }
18885
return emptyList()
18986
}
19087
}

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/EventHandler.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class EventHandler() : AutoCloseable {
6868
fun emit(event: Event) {
6969
val key = event.toKey() ?: return
7070
val publisher = publishers[key] ?: error("no publisher for topic '${key}'")
71-
val payload = ZBytes.from(EventExchange(event).toBytes())
71+
val payload = ZBytes.from(EventExchange.toBytes(event))
7272

7373
publisher.put(payload).onFailure { error("failed to send event '$event'") }
7474
}
@@ -104,7 +104,7 @@ class EventHandler() : AutoCloseable {
104104
private fun handleIncoming(sample: Sample) {
105105
runCatching {
106106
val bytes = sample.payload.toBytes()
107-
val event = EventExchange.fromBytes(bytes).event
107+
val event = EventExchange.fromBytes(bytes)
108108

109109
propagate(event)
110110
}

src/main/kotlin/at/ac/uibk/dps/cirrina/execution/object/State.kt

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,14 @@ class State
1212
internal constructor(
1313
@Assisted val specification: StateSpec,
1414
@Assisted private val parent: StateMachine,
15-
private val commandFactory: ActionCommandFactory,
1615
) : Scope {
17-
private val entry = specification.entry.toTopologicalList()
18-
private val during = specification.during.toTopologicalList()
19-
private val exit = specification.exit.toTopologicalList()
16+
val entryActions = specification.entry.toTopologicalList()
17+
val duringActions = specification.during.toTopologicalList()
18+
val exitActions = specification.exit.toTopologicalList()
2019
val timeout = specification.after.toTopologicalList().filterIsInstance<TimeoutAction>()
2120

2221
override val extent: Extent by lazy { parent.extent.extend(Context.from(specification.static)) }
2322

24-
fun getEntryActionCommands(ctx: CommandExecutionContext) =
25-
entry.map { commandFactory.create(it, ctx) }
26-
27-
fun getDuringActionCommands(ctx: CommandExecutionContext) =
28-
during.map { commandFactory.create(it, ctx) }
29-
30-
fun getExitActionCommands(ctx: CommandExecutionContext) =
31-
exit.map { commandFactory.create(it, ctx) }
32-
3323
private fun <V, E> Graph<V, E>.toTopologicalList(): List<V> =
3424
TopologicalOrderIterator(this).asSequence().toList()
3525

0 commit comments

Comments
 (0)