Created
December 19, 2019 18:33
-
-
Save mike-neck/de52315c2abf66662adc8bb5e2deba36 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
. ____ _ __ _ _ | |
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ | |
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ | |
\\/ ___)| |_)| | | | | || (_| | ) ) ) ) | |
' |____| .__|_| |_|_| |_\__, | / / / / | |
=========|_|==============|___/=/_/_/_/ | |
:: Spring Boot :: (v2.2.2.RELEASE) | |
2019-12-20 00:10:03.221 INFO 7504 --- [ main] com.example.MainKt : Starting MainKt on app.local with PID 7504 (/Users/mike/spring-scheduled-task-example) | |
2019-12-20 00:10:03.224 INFO 7504 --- [ main] com.example.MainKt : No active profile set, falling back to default profiles: default | |
2019-12-20 00:10:03.906 INFO 7504 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler' | |
2019-12-20 00:10:04.157 INFO 7504 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' | |
2019-12-20 00:10:04.447 INFO 7504 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080 | |
2019-12-20 00:10:04.451 INFO 7504 --- [ main] com.example.MainKt : Started MainKt in 1.411 seconds (JVM running for 2.111) | |
2019-12-20 00:10:14.458 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:10:14.460 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: FIVE_MIN | |
2019-12-20 00:11:04.479 INFO 7504 --- [ parallel-1] com.example.DurableTask : finish task: FIVE_MIN | |
2019-12-20 00:11:04.482 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:11:04.480Z in 50022ms | |
2019-12-20 00:11:04.489 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:11:24.480Z | |
2019-12-20 00:11:24.486 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:11:24.486 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: THREE_MIN | |
2019-12-20 00:11:54.491 INFO 7504 --- [ parallel-2] com.example.DurableTask : finish task: THREE_MIN | |
2019-12-20 00:11:54.491 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:11:54.491Z in 30005ms | |
2019-12-20 00:11:54.493 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:12:24.491Z | |
2019-12-20 00:12:24.493 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:12:24.493 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: ONE_MIN | |
2019-12-20 00:12:34.496 INFO 7504 --- [ parallel-3] com.example.DurableTask : finish task: ONE_MIN | |
2019-12-20 00:12:34.497 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:12:34.497Z in 10004ms | |
2019-12-20 00:12:34.497 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:13:34.497Z | |
2019-12-20 00:13:34.501 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:13:34.502 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: FIVE_MIN | |
2019-12-20 00:14:24.508 INFO 7504 --- [ parallel-4] com.example.DurableTask : finish task: FIVE_MIN | |
2019-12-20 00:14:24.508 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:14:24.508Z in 50007ms | |
2019-12-20 00:14:24.508 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:14:44.508Z | |
2019-12-20 00:14:44.509 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:14:44.509 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: THREE_MIN | |
2019-12-20 00:15:14.514 INFO 7504 --- [ parallel-5] com.example.DurableTask : finish task: THREE_MIN | |
2019-12-20 00:15:14.514 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:15:14.514Z in 30005ms | |
2019-12-20 00:15:14.515 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:15:44.514Z | |
2019-12-20 00:15:44.515 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:15:44.516 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: ONE_MIN | |
2019-12-20 00:15:54.521 INFO 7504 --- [ parallel-6] com.example.DurableTask : finish task: ONE_MIN | |
2019-12-20 00:15:54.521 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:15:54.521Z in 10006ms | |
2019-12-20 00:15:54.522 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:16:54.521Z | |
2019-12-20 00:16:54.526 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:16:54.526 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: THREE_MIN | |
2019-12-20 00:17:24.530 INFO 7504 --- [ parallel-7] com.example.DurableTask : finish task: THREE_MIN | |
2019-12-20 00:17:24.531 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:17:24.531Z in 30005ms | |
2019-12-20 00:17:24.531 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:17:54.531Z | |
2019-12-20 00:17:54.531 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:17:54.532 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: FIVE_MIN | |
2019-12-20 00:18:44.537 INFO 7504 --- [ parallel-8] com.example.DurableTask : finish task: FIVE_MIN | |
2019-12-20 00:18:44.537 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:18:44.537Z in 50006ms | |
2019-12-20 00:18:44.537 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:19:04.537Z | |
2019-12-20 00:19:04.538 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:19:04.538 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: ONE_MIN | |
2019-12-20 00:19:14.539 INFO 7504 --- [ parallel-9] com.example.DurableTask : finish task: ONE_MIN | |
2019-12-20 00:19:14.540 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:19:14.540Z in 10002ms | |
2019-12-20 00:19:14.540 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:20:14.540Z | |
2019-12-20 00:20:14.546 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:20:14.546 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: TWO_MIN | |
2019-12-20 00:20:34.552 INFO 7504 --- [ parallel-10] com.example.DurableTask : finish task: TWO_MIN | |
2019-12-20 00:20:34.553 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:20:34.553Z in 20007ms | |
2019-12-20 00:20:34.553 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:21:04.553Z | |
2019-12-20 00:21:04.556 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:21:04.556 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: ONE_MIN | |
2019-12-20 00:21:14.562 INFO 7504 --- [ parallel-11] com.example.DurableTask : finish task: ONE_MIN | |
2019-12-20 00:21:14.563 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:21:14.563Z in 10007ms | |
2019-12-20 00:21:14.563 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:22:14.563Z | |
2019-12-20 00:22:14.567 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task start | |
2019-12-20 00:22:14.567 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task: TWO_MIN | |
2019-12-20 00:22:34.569 INFO 7504 --- [ parallel-12] com.example.DurableTask : finish task: TWO_MIN | |
2019-12-20 00:22:34.569 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : task finished: 2019-12-19T15:22:34.569Z in 20002ms | |
2019-12-20 00:22:34.569 INFO 7504 --- [ scheduling-1] com.example.TaskConfig : next task will start at 2019-12-19T15:23:04.569Z | |
2019-12-20 00:22:35.203 INFO 7504 --- [extShutdownHook] o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService 'taskScheduler' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example | |
import org.springframework.boot.autoconfigure.SpringBootApplication | |
import org.springframework.boot.runApplication | |
import org.springframework.scheduling.annotation.EnableScheduling | |
@EnableScheduling | |
@SpringBootApplication | |
class Main | |
fun main(args: Array<String>) { | |
runApplication<Main>(*args) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example | |
import java.util.function.Predicate | |
sealed class Step<T, R> { | |
fun on(p: (T) -> Boolean): TransformStep<T, R> = on(Predicate { p(it) }) | |
abstract fun on(predicate: Predicate<T>): TransformStep<T, R> | |
abstract fun others(f: (T) -> R): R | |
} | |
interface TransformStep<T, R> { | |
fun then(f: (T) -> R): Step<T, R> | |
} | |
data class NotMatched<T, R>(val original: T): Step<T, R>() { | |
override fun on(predicate: Predicate<T>): TransformStep<T, R> = | |
if (predicate.test(original)) object : TransformStep<T, R> { | |
override fun then(f: (T) -> R): Step<T, R> = Matched(f(original)) | |
} | |
else object: TransformStep<T, R> { | |
override fun then(f: (T) -> R): Step<T, R> = NotMatched(original) | |
} | |
override fun others(f: (T) -> R): R = f(original) | |
} | |
data class Matched<T, R>(val transformed: R): Step<T, R>() { | |
override fun on(predicate: Predicate<T>): TransformStep<T, R> = object: TransformStep<T, R> { | |
override fun then(f: (T) -> R): Step<T, R> = this@Matched | |
} | |
override fun others(f: (T) -> R): R = transformed | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example | |
import org.slf4j.Logger | |
import org.slf4j.LoggerFactory | |
import org.springframework.boot.CommandLineRunner | |
import org.springframework.context.annotation.Bean | |
import org.springframework.context.annotation.Configuration | |
import org.springframework.scheduling.TaskScheduler | |
import org.springframework.scheduling.Trigger | |
import org.springframework.scheduling.TriggerContext | |
import reactor.core.Disposable | |
import reactor.core.publisher.Mono | |
import java.time.Duration | |
import java.time.Instant | |
import java.util.* | |
import java.util.concurrent.CountDownLatch | |
import java.util.concurrent.ThreadLocalRandom | |
enum class DurableTask(private val size: Int) { | |
ONE_MIN(1), | |
TWO_MIN(2), | |
THREE_MIN(3), | |
FIVE_MIN(5), | |
; | |
fun run(): Mono<Unit> = | |
Mono.defer { Mono.delay(Duration.ofSeconds(size * lengthUnit)) } | |
.thenReturn(Unit) | |
.doOnTerminate { logger.info("finish task: {}", this) } | |
companion object { | |
val logger: Logger = LoggerFactory.getLogger(DurableTask::class.java) | |
const val lengthUnit: Long = 10L | |
private val length: Int = values().size | |
fun random(): DurableTask = values()[ThreadLocalRandom.current().nextInt(length)] | |
} | |
} | |
val Instant.date: Date get() = Date.from(this) | |
fun duration(from: Date, to: Date): Duration = Duration.between(from.toInstant(), to.toInstant()) | |
val Disposable.asCloseable: AutoCloseable get() = AutoCloseable { this.dispose() } | |
inline fun <A, B, C> Pair<A, B>.map(f: (A, B) -> C): C = f(this.first, this.second) | |
inline fun <A : Any, B : Any> A?.with(fb: () -> B?): Pair<A, B>? = | |
if (this == null) null | |
else fb() | |
.let { b -> | |
when (b) { | |
null -> null | |
else -> this to b | |
} | |
} | |
fun <A : Any, B : Any> A.determineConditionally(): Step<A, B> = NotMatched(this) | |
inline fun <A: Any, B: Any, C: Any> A.determineConditionally(f: (A) -> B): Step<B, C> = f(this).determineConditionally() | |
@Configuration | |
class TaskConfig { | |
@Bean | |
fun commandLineRunner(taskScheduler: TaskScheduler): CommandLineRunner = | |
CommandLineRunner { | |
taskScheduler.schedule(Runnable { | |
logger.info("task start") | |
val latch = CountDownLatch(1) | |
DurableTask | |
.random() | |
.also { logger.info("next task: {}", it) } | |
.run() | |
.doOnTerminate { latch.countDown() } | |
.subscribe().asCloseable | |
.use { latch.await() } | |
}, Companion) | |
} | |
companion object : Trigger { | |
private val logger: Logger = LoggerFactory.getLogger(TaskConfig::class.java) | |
override fun nextExecutionTime(triggerContext: TriggerContext): Date = | |
triggerContext.lastActualExecutionTime().with { triggerContext.lastCompletionTime() } | |
?.map { start, end -> end.toInstant() to duration(start, end) } | |
?.also { logger.info("task finished: {} in {}ms", it.first, it.second.toMillis()) } | |
?.determineConditionally<Pair<Instant, Duration>, PreviousTask, Instant> { PreviousTask(it.first, it.second) } | |
?.on { FIVE_MIN < it.timeElapsed }?.then { it.finishedAt + ONE_MIN + ONE_MIN } | |
?.on { TWO_MIN < it.timeElapsed }?.then { it.finishedAt + ONE_MIN + TWO_MIN } | |
?.on { ONE_MIN < it.timeElapsed }?.then { it.finishedAt + ONE_MIN + FIVE_MIN } | |
?.others { it.finishedAt + ONE_MIN + TWO_MIN + FIVE_MIN } | |
?.also { logger.info("next task will start at {}", it) } | |
?.date ?: (Instant.now() + ONE_MIN).date | |
private val FIVE_MIN: Duration = Duration.ofSeconds(5 * DurableTask.lengthUnit) | |
private val TWO_MIN: Duration = Duration.ofSeconds(2 * DurableTask.lengthUnit) | |
private val ONE_MIN: Duration = Duration.ofSeconds(1 * DurableTask.lengthUnit) | |
} | |
} | |
data class PreviousTask(val finishedAt: Instant, val timeElapsed: Duration) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment