I have a project where i use Reactor with Spring Cloud Gateway (version Hoxton.SR5)
I wanted to implement a cache for data that is queried by the gateway from other services.
From what i've read, the #Cacheable annotations aren't compatible with reactor 3.3, so i went to the CacheMono in combination with Caffeine.
The code i'm trying to write should do a fairly simple job:
Check Cache for entry
If missed, query the service
Create a mono
After mono returns, put the value inside the cache
If cache hits, just return that Mono
The abomination of code (function getFromSource returns a Mono via Webclient):
var myCache: Cache<String, DomainObject> = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.maximumSize(1000)
.build()
fun get(id: String): Mono<DomainObject> {
return CacheMono
.lookup(
{ key ->
Mono
.justOrEmpty(myCache.getIfPresent(key))
.map {s ->
Signal.next(s)
}
},
id
)
.onCacheMissResume {
getFromSource(id)
}
.andWriteWith { (key, signal) ->
Mono.fromRunnable {
Optional
.ofNullable(signal.get())
.ifPresent { value: DomainObject ->
myCache.put(key, value)
}
}
}
}
It's complaining about the following:
Type inference failed: fun <KEY : Any!, VALUE : Any!> lookup(p0: (KEY!) -> Mono<Signal<out VALUE!>!>!, p1: KEY): CacheMono.MonoCacheBuilderCacheMiss<KEY!, VALUE!>
cannot be applied to
((String!) -> Mono<Signal<#Nullable DomainObject!>!>,String)
Help is gladly appreciated.
Provide type parameters to lookup method:
.lookup<String, DomainObject>(
I have a Kafka processor that is defined like this.
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.common.serialization.StringDeserializer
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Bean
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
import reactor.kafka.receiver.KafkaReceiver
import reactor.kafka.receiver.ReceiverOptions
import reactor.kafka.receiver.ReceiverRecord
import reactor.kotlin.core.publisher.toMono
import reactor.util.retry.Retry
import java.time.Duration
import java.util.*
#Component
class KafkaProcessor {
private val logger = LoggerFactory.getLogger(javaClass)
private val consumerProps = hashMapOf(
ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG to StringDeserializer::javaClass,
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG to StringDeserializer::javaClass,
ConsumerConfig.GROUP_ID_CONFIG to "groupId",
ConsumerConfig.AUTO_OFFSET_RESET_CONFIG to "earliest",
ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG to "localhost:9092"
)
private val receiverOptions = ReceiverOptions.create<String, String>(consumerProps)
.subscription(Collections.singleton("some-topic"))
.commitInterval(Duration.ofSeconds(1))
.commitBatchSize(1000)
.maxCommitAttempts(1)
private val kafkaReceiver: KafkaReceiver<String, String> = KafkaReceiver.create(receiverOptions)
#Bean
fun processKafkaMessages(): Unit {
kafkaReceiver.receive()
.groupBy { m -> m.receiverOffset().topicPartition() }
.flatMap { partitionFlux ->
partitionFlux.publishOn(Schedulers.elastic())
.concatMap { receiverRecord ->
processRecord(receiverRecord)
.map { it.receiverOffset().acknowledge() }
}
}
.retryWhen(
Retry.backoff(3, Duration.ofSeconds(1))
.maxBackoff(Duration.ofSeconds(3))
.doBeforeRetry { rs ->
logger.warn("Retrying: ${rs.totalRetries() + 1}/3 due to ${rs.failure()}")
}
.onRetryExhaustedThrow { _, u ->
logger.error("All ${u.totalRetries() + 1} attempts failed with the last exception: ${u.failure()}")
u.failure()
}
)
.subscribe()
}
private fun processRecord(record: ReceiverRecord<String, String>): Mono<ReceiverRecord<String, String>> {
return record.toMono()
}
}
Sometimes, I got this error.
org.apache.kafka.clients.consumer.RetriableCommitFailedException: Offset commit failed with a retriable exception. You should retry committing the latest consumed offsets.
Caused by: org.apache.kafka.common.errors.TimeoutException: The request timed out.
The first retry looks like this.
Retrying: 1/3 due to org.apache.kafka.clients.consumer.RetriableCommitFailedException: Offset commit failed with a retriable exception. You should retry committing the latest consumed offsets
The second and third look like this.
Retrying: 2/3 due to reactor.core.Exceptions$ReactorRejectedExecutionException: Scheduler unavailable
Retrying: 3/3 due to reactor.core.Exceptions$ReactorRejectedExecutionException: Scheduler unavailable
And once all the 3 retries are exhausted, the message looks like this.
All 4 attempts failed with the last exception: reactor.core.Exceptions$ReactorRejectedExecutionException: Scheduler unavailable
When I do get that error, I need to restart the application in order to reconnect to the Kafka broker and commit the record.
I am aware that by setting maxCommitAttempts to 1 means that once it hits a RetriableCommitFailedException, it won't retry again. I thought that the retryWhen clause I put in the end of the processKafkaMessages() function would do the trick so that the pipeline can recover by itself.
The reason I set the maxCommitAttempts is because it does not have the retry with backoff as discussed here and the default 100 max commit attempts is done within 10ms. So, I thought I should write my own retry logic with a backoff.
The question is, how should I do the retry with backoff for the auto commit correctly? And is it possible to write a unit test for that using EmbeddedKafka?
Language: Kotlin
Reactor Kafka library: io.projectreactor.kafka:reactor-kafka:1.2.2.RELEASE
retryWhen() merely attempts to re-subscribe. Since the Kafka consumer is in error state, it will reject the re-subscription. You need to defer the kafkaReceiver.receive() call, thus:
Flux.defer(() -> kafkaReceiver.receive())
.groupBy { m -> m.receiverOffset().topicPartition() }
// etc
Thus re-subscription will call kafkaReceiver.receive() again and create a new consumer.
I recently started working with corda and try to create POC. There is one requirement is to create corda node at runtime. I have searched into corda documentation, but bad luck.
Is there any way to create nodes at runtime?
You could have a look at net.corda.testing.driver which can be used to start up a set of test nodes for integration testing
class DriverBasedTest {
private val bankA = TestIdentity(CordaX500Name("BankA", "", "GB"))
private val bankB = TestIdentity(CordaX500Name("BankB", "", "US"))
#Test
fun `node test`() = withDriver {
// Start a pair of nodes and wait for them both to be ready.
val (partyAHandle, partyBHandle) = startNodes(bankA, bankB)
// From each node, make an RPC call to retrieve another node's name from the network map, to verify that the
// nodes have started and can communicate.
// This is a very basic test: in practice tests would be starting flows, and verifying the states in the vault
// and other important metrics to ensure that your CorDapp is working as intended.
assertEquals(bankB.name, partyAHandle.resolveName(bankB.name))
assertEquals(bankA.name, partyBHandle.resolveName(bankA.name))
}
}
From here -> https://github.com/corda/corda-settler/blob/master/cordapp/src/integrationTest/kotlin/com/template/DriverBasedTest.kt
I need to run some jobs in a cluster, only one at a time.
Because my team uses Hazelcast, I ended up with a solution based on
Hazelcast ILock implementation. For the purpose of the question, I am going to make a generalisation about it. Let's suppose we have the following interfaces (that could be easily implemented e.g. by Hazelcast or Reddison (Redis)):
public interface MyDistributedLock {
boolean lock();
void unlock();
boolean isLockedByCurrentThread();
}
public interface MyLockDistributedFactory {
MyDistributedLock getLock(String name);
}
And lock method waiting if lock cannot be acquired:
private Mono<Void> lock(String name, Publisher<?> publisher, MyLockDistributedFactory myLockFactory) {
// important to release lock on the same thread as
// it was aquired
Scheduler scheduler = Schedulers.newSingle(name.toLowerCase());
return Mono.defer(() -> Mono.just(myLockFactory.getLock(name)))
publishOn(scheduler)
.doOnNext(MyDistributedLock::lock)
.doOnNext(lock -> LOGGER.info("Process acquired lock for resource {}", name))
.flatMapMany(lock -> Flux.from(publisher))
.publishOn(scheduler)
.doFinally(signalType -> {
MyDistributedLock lock = myLockFactory.getLock(name);
if (signalType == SignalType.CANCEL) {
// cancel ignores publishOn
scheduler.schedule(() -> {
lock.unlock();
LOGGER.info("Process released lock for resource {} due to signal type {}", name, signalType);
});
} else if (lock.isLockedByCurrentThread()) {
lock.unlock();
LOGGER.info("Process released lock for resource {} due to signal type {}", name, signalType);
}
})
.then();
}
And example of some job
private Mono<Void> someJobRunEveryOneHourOnEveryNodeInCluster() {
MyLockDistributedFactory hazelcast = ...;
return lock("some-job", Flux.just(1,2), hazelcast)
.repeatWhen(afterOneHour());
}
I wonder whether this is a good approach of using Project reactor (and correct implementation) or it should be done in a different way. Please advice.
it is a correct approach when using Reactor, because you took care of offsetting the blocking portion into a dedicated Scheduler/Thread.
But I'd say mutually exclusive code like this is not a very good fit for reactive programming in general: you lose one of the key benefits of doing more with less threads, you risk blocking other parts of the application should you forget to publishOn a dedicated thread, etc...
I know an alternative of reflection which is using javassist, but using javassist is a little bit complex. And because of lambda or some other features in koltin, the javassist doesn't work well sometimes. So is there any other way to iterate all fields of a data class without using reflection.
There are two ways. The first is relatively easy, and is essentially what's mentioned in the comments: assuming you know how many fields there are, you can unpack it and throw that into a list, and iterate over those. Or alternatively use them directly:
data class Test(val x: String, val y: String) {
fun getData() : List<Any> = listOf(x, y)
}
data class Test(val x: String, val y: String)
...
val (x, y) = Test("x", "y")
// And optionally throw those in a list
Although iterating like this is a slight extra step, this is at least one way you can relatively easy unpack a data class.
If you don't know how many fields there are (or you don't want to refactor), you have two options:
The first is using reflection. But as you mentioned, you don't want this.
That leaves a second, somewhat more complicated preprocessing option: annotations. Note that this only works with data classes you control - beyond that, you're stuck with reflection or implementations from the library/framework coder.
Annotations can be used for several things. One of which is metadata, but also code generation. This is a somewhat complicated alternative, and requires an additional module in order to get compile order right. If it isn't compiled in the right order, you'll end up with unprocessed annotations, which kinda defeats the purpose.
I've also created a version you can use with Gradle, but that's at the end of the post and it's a shortcut to implementing it yourself.
Note that I have only tested this with a pure Kotlin project - I've personally had problems with annotations between Java and Kotlin (although that was with Lombok), so I do not guarantee this will work at compile time if called from Java. Also note that this is complex, but avoids runtime reflection.
Explanation
The main issue here is a certain memory concern. This will create a new list every time you call the method, which makes it very similar to the method used by enums.
Local testing over 10000 iterations also show a general consistency of ~200 milliseconds to execute my approach, versus roughly 600 for reflection. However, for one iteration, mine uses ~20 milliseconds, where as reflection uses between 400 and 500 milliseconds. On one run, reflection took 1500 (!) milliseconds, while my approach took 18 milliseconds.
See also Java Reflection: Why is it so slow?. This appears to affect Kotlin as well.
The memory impact of creating a new list every time it's called can be noticeable though, but it'll also be collected so it shouldn't be that big a problem.
For reference, the code used for benchmarking (this will make sense after the rest of the post):
#AutoUnpack data class ExampleDataClass(val x: String, val y: Int, var m: Boolean)
fun main(a: Array<String>) {
var mine = 0L
var reflect = 0L
// for(i in 0 until 10000) {
var start = System.currentTimeMillis()
val cls = ExampleDataClass("example", 42, false)
for (field in cls) {
println(field)
}
mine += System.currentTimeMillis() - start
start = System.currentTimeMillis()
for (prop in ExampleDataClass::class.memberProperties) {
println("${prop.name} = ${prop.get(cls)}")
}
reflect += System.currentTimeMillis() - start
// }
println(mine)
println(reflect)
}
Setting up from scratch
This bases itself around two modules: a consumer module, and a processor module. The processor HAS to be in a separate module. It needs to be compiled separately from the consumer for the annotations to work properly.
First of all, your consumer project needs the annotation processor:
apply plugin: 'kotlin-kapt'
Additionally, you need to add stub generation. It complains it's unused while compiling, but without it, the generator seems to break for me:
kapt {
generateStubs = true
}
Now that that's in order, create a new module for the unpacker. Add the Kotlin plugin if you didn't already. You do not need the annotation processor Gradle plugin in this project. That's only needed by the consumer. You do, however, need kotlinpoet:
implementation "com.squareup:kotlinpoet:1.2.0"
This is to simplify aspects of the code generation itself, which is the important part here.
Now, create the annotation:
#Retention(AnnotationRetention.SOURCE)
#Target(AnnotationTarget.CLASS)
annotation class AutoUnpack
This is pretty much all you need. The retention is set to source because it has no value at runtime, and it only targets compile time.
Next, there's the processor itself. This is somewhat complicated, so bear with me. For reference, this uses the javax.* packages for annotation processing. Android note: this might work assuming you can plug in a Java module on a compileOnly scope without getting the Android SDK restrictions. As I mentioned earlier, this is mainly for pure Kotlin; Android might work, but I haven't tested that.
Anyways, the generator:
Because I couldn't find a way to generate the method into the class without touching the rest (and because according to this, that isn't possible), I'm going with an extension function generation approach.
You'll need a class UnpackCodeGenerator : AbstractProcessor(). In there, you'll first need two lines of boilerplate:
override fun getSupportedAnnotationTypes(): MutableSet<String> = mutableSetOf(AutoUnpack::class.java.name)
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
Moving on, there's the processing. Override the process function:
override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment): Boolean {
// Find elements with the annotation
val annotatedElements = roundEnv.getElementsAnnotatedWith(AutoUnpack::class.java)
if(annotatedElements.isEmpty()) {
// Self-explanatory
return false;
}
// Iterate the elements
annotatedElements.forEach { element ->
// Grab the name and package
val name = element.simpleName.toString()
val pkg = processingEnv.elementUtils.getPackageOf(element).toString()
// Then generate the class
generateClass(name,
if (pkg == "unnamed package") "" else pkg, // This is a patch for an issue where classes in the root
// package return package as "unnamed package" rather than empty,
// which breaks syntax because "package unnamed package" isn't legal.
element)
}
// Return true for success
return true;
}
This just sets up some of the later framework. The real magic happens in the generateClass function:
private fun generateClass(className: String, pkg: String, element: Element){
val elements = element.enclosedElements
val classVariables = elements
.filter {
val name = if (it.simpleName.contains("\$delegate"))
it.simpleName.toString().substring(0, it.simpleName.indexOf("$"))
else it.simpleName.toString()
it.kind == ElementKind.FIELD // Find fields
&& Modifier.STATIC !in it.modifiers // that aren't static (thanks to sebaslogen for issue #1: https://github.com/LunarWatcher/KClassUnpacker/issues/1)
// Additionally, we have to ignore private fields. Extension functions can't access these, and accessing
// them is a bad idea anyway. Kotlin lets you expose get without exposing set. If you, by default, don't
// allow access to the getter, there's a high chance exposing it is a bad idea.
&& elements.any { getter -> getter.kind == ElementKind.METHOD // find methods
&& getter.simpleName.toString() ==
"get${name[0].toUpperCase().toString() + (if (name.length > 1) name.substring(1) else "")}" // that matches the getter name (by the standard convention)
&& Modifier.PUBLIC in getter.modifiers // that are marked public
}
} // Grab the variables
.map {
// Map the name now. Also supports later filtering
if (it.simpleName.endsWith("\$delegate")) {
// Support by lazy
it.simpleName.subSequence(0, it.simpleName.indexOf("$"))
} else it.simpleName
}
if (classVariables.isEmpty()) return; // Self-explanatory
val file = FileSpec.builder(pkg, className)
.addFunction(FunSpec.builder("iterator") // For automatic unpacking in a for loop
.receiver(element.asType().asTypeName().copy()) // Add it as an extension function of the class
.addStatement("return listOf(${classVariables.joinToString(", ")}).iterator()") // add the return statement. Create a list, push an iterator.
.addModifiers(KModifier.PUBLIC, KModifier.OPERATOR) // This needs to be public. Because it's an iterator, the function also needs the `operator` keyword
.build()
).build()
// Grab the generate directory.
val genDir = processingEnv.options["kapt.kotlin.generated"]!!
// Then write the file.
file.writeTo(File(genDir, "$pkg/${element.simpleName.replace("\\.kt".toRegex(), "")}Generated.kt"))
}
All of the relevant lines have comments explaining use, in case you're not familiar with what this does.
Finally, in order to get the processor to process, you need to register it. In the module for the generator, add a file called javax.annotation.processing.Processor under main/resources/META-INF/services. In there you write:
com.package.of.UnpackCodeGenerator
From here, you need to link it using compileOnly and kapt. If you added it as a module to your project, you can do:
kapt project(":ClassUnpacker")
compileOnly project(":ClassUnpacker")
Alternative source setup:
Like I mentioned earlier, I bundled this into a jar for convenience. It's under the same license as SO uses (CC-BY-SA 3.0), and it contains the exact same code as in the answer (although compiled into a single project).
If you want to use this one, just add the Jitpack repo:
repositories {
// Other repos here
maven { url 'https://jitpack.io' }
}
And hook it up with:
kapt 'com.github.LunarWatcher:KClassUnpacker:v1.0.1'
compileOnly "com.github.LunarWatcher:KClassUnpacker:v1.0.1"
Note that the version here may not be up to date: the up to date list of versions is available here. The code in the post still aims to reflect the repo, but versions aren't really important enough to edit every time.
Usage
Regardless of which way you ended up using to get the annotations, the usage is relatively easy:
#AutoUnpack data class ExampleDataClass(val x: String, val y: Int, var m: Boolean)
fun main(a: Array<String>) {
val cls = ExampleDataClass("example", 42, false)
for(field in cls) {
println(field)
}
}
This prints:
example
42
false
Now you have a reflection-less way of iterating fields.
Note that local testing has been done partially with IntelliJ, but IntelliJ doesn't seem to like me - I've had various failed builds where gradlew clean && gradlew build from a command line oddly works fine. I'm not sure whether this is a local problem, or if this is a general problem, but you might have some issues like this if you build from IntelliJ.
Also, you might get errors if the build fails. The IntelliJ linter builds on top of the build directory for some sources, so if the build fails and the file with the extension function isn't generated, that'll cause it to appear as an error. Building usually fixes this when I tested (with both modules and from Jitpack).
You'll also likely have to enable the annotation processor setting if you use Android Studio or IntelliJ.
here is another idea, that i came up with, but am not satisfied with...but it has some pros and cons:
pros:
adding/removing fields to/from the data class causes compiler errors at field-iteration sites
no boiler-plate code needed
cons:
won't work if default values are defined for arguments
declaration:
data class Memento(
val testType: TestTypeData,
val notes: String,
val examinationTime: MillisSinceEpoch?,
val administeredBy: String,
val signature: SignatureViewHolder.SignatureData,
val signerName: String,
val signerRole: SignerRole
) : Serializable
iterating through all fields (can use this directly at call sites, or apply the Visitor pattern, and use this in the accept method to call all the visit methods):
val iterateThroughAllMyFields: Memento = someValue
Memento(
testType = iterateThroughAllMyFields.testType.also { testType ->
// do something with testType
},
notes = iterateThroughAllMyFields.notes.also { notes ->
// do something with notes
},
examinationTime = iterateThroughAllMyFields.examinationTime.also { examinationTime ->
// do something with examinationTime
},
administeredBy = iterateThroughAllMyFields.administeredBy.also { administeredBy ->
// do something with administeredBy
},
signature = iterateThroughAllMyFields.signature.also { signature ->
// do something with signature
},
signerName = iterateThroughAllMyFields.signerName.also { signerName ->
// do something with signerName
},
signerRole = iterateThroughAllMyFields.signerRole.also { signerRole ->
// do something with signerRole
}
)