Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@Scheduled doesn't work with custom event IDs #96

Open
msiemens opened this issue Feb 13, 2021 · 1 comment
Open

@Scheduled doesn't work with custom event IDs #96

msiemens opened this issue Feb 13, 2021 · 1 comment

Comments

@msiemens
Copy link

Originally posted at #82

@Scheduled events appear to not work at all on AWS Lambda, when custom event IDs are used.

Summary: The short summary is that making scheduled events with custom IDs work (which would be required to not plaster one's AWS account with merged-X lambdas), some non-minor refactoring needs to be done.

As far as I can see from my initial investigation, the issue is as follows:

  1. When the LambdaHandler gets the scheduled event, it contains a resource identifier such as arn:aws:events:us-east-1:123456789012:rule/general-analyseOffers. As the general prefix is contained in the resource ID, the EventsStorage is called to fetch the target function:

    val key = resource.substring(resource.lastIndexOf(ScheduledEventType.General.prefix))
    logger.trace("Executing scheduled lambda with key $key")
    EventsStorage[key]?.let { FunctionCaller.call(it, emptyMap()) }
    logger.trace("Scheduled lambda with key $key was executed")
    Note that due to the substring part, the resource ID including the general prefix is passed as an argument, in this case general-analyseOffers.

  2. The EventsStorage scans for all functions with the @Scheduled annotation and notes their ID:

    for ((ids, method, _) in EventsReflectionScanner.getEvents()) {
    val kFunc = method.kotlinFunction!!
    for (id in ids) {
    cache[id] = kFunc
    logger.debug("Saved with key $id function ${kFunc.name} for annotation ${Scheduled::class.simpleName}")
    }
    }
    If there is a custom ID, it is used (analyseOffers in our case), otherwise an ID is generated that that includes the general prefix (e.g. general-12345):
    return annotation.id.takeIf { it.isNotBlank() }?.let { setOf(it) } ?: run {
    val klass = declaringClass.kotlin.qualifiedName!!
    setOf("$klass.$name", "${klass.substringBeforeLast(".")}.$name")
    .map { "${ScheduledEventType.General.prefix}-${it.hashCode().absoluteValue}" }
    .toSet()
    }

  3. The event is now looked up using the prefixed ID (general-analyseOffers):

    Now, as the Cloudwatch event includes the prefix, but the EventsStorage stored the event using the custom ID which does NOT include the prefix (analyseOffers), the lookup fails silently and the function is NOT called:
    EventsStorage[key]?.let { FunctionCaller.call(it, emptyMap()) }

And it gets even more complicated. The rule name, that is generated for AWS, is actually modified to fit the AWS naming rules:

fun aws(name: Iterable<String>): String {
return (schema.config.prefix.plusIterable(name)).flatMap { Text.deall(it) }.joinToString(separator = "-") { it.toLowerCase() }
}
This means that for example @Scheduled(Scheduled.everyHour, 'com.example.scheduled') creates a Cloudwatch rule with the name general-com-example-scheduled. Thus during event registration, we need to apply the very same rules to the event ID to make sure that when the event is called, the ID lookup will succeed. The issue with that is that at the moment the logic for generating AWS-compatible names is part of the Gradle plugin, but not of the Kotless runtime library. Basically, the code above and its depending classes (mainly io.kotless.utils.Text from kotless-engine) would have to be moved to a shared library so it can be called both at compile-time and at runtime.

@msiemens
Copy link
Author

msiemens commented Feb 13, 2021

To summarize a propsed solution:

  1. Move the logic for making event IDs AWS-compatible to some shared library available at both compile-time and runtime
  2. When scanning for @Scheduled events and encourering custom event IDs, use prefix them with ScheduledEventType.General.prefix AND make the event ID AWS-compatible using the same logic as at compile-time

If possible it also would be great to have some form of log message, when an event lookup fails. Failing silently is a really unfortunate option in my opinion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant