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

[core] Ensure onClose callbacks are invoked in reversed initialization order #2020

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

shchuko
Copy link

@shchuko shchuko commented Oct 10, 2024

Fixes #1510

onClose callback can be used to release allocated resources
like DB connections. Discussion #1507

Some beans can depend on each other.

An example: classic Web application consists of Controller, Service, Database.

In this case, instantiation order is:
  1. Database
  2. Service - implements business logic, requires Database
  3. Controller - REST controller, requires Service

On application graceful shutdown, onClose invocation order must be reversed:
  1. Controller - created last, destroyed first
  2. Service - destroyed after Controller to avoid use-after-close
  3. Database - destroyed after Service to avoid use-after-close

This commit delivers implementation of onClose calls ordering.

When instance factory creates its first instance, it means that all dependencies are also created. So it's enough to sort factories by 'first instance creation,' and then reverse the sequence when invoking onClose.

Implementation:

  • global 'instance creation order' atomic counter is created
  • each instance factory has 'instance creation order' position
  • when factory creates its first instance
    • 'instance creation order' counter is incremented
    • the counter's value is saved into 'instance creation order' position of the factory
  • factories can be sorted by 'instance creation order' position, this can be used to order onClose calls

@shchuko shchuko force-pushed the yaroshchuk/on-close-order branch 3 times, most recently from 6081a6c to 9401779 Compare October 11, 2024 15:37
@shchuko shchuko changed the title [core] Ensure onClose callbacks are invoked in reversed initializatio… [core] Ensure onClose callbacks are invoked in reversed initialization order Oct 11, 2024
@shchuko shchuko marked this pull request as ready for review October 11, 2024 15:38
`onClose` callback can be used to release allocated resources
like DB connections.

Some beans can depend on each other.

An example: classic Web application consists of
`Controller`, `Service`, `Database`.

In this case, instantiation order is:
  1. `Database`
  2. `Service` - implements business logic, requires `Database`
  3. `Controller` - REST controller, requires `Service`

On application graceful shutdown, `onClose` invocation order
must be reversed:
  1. `Controller` - created last, destroyed first
  2. `Service` - destroyed after `Controller` to avoid use-after-close
  3. `Database` - destroyed after `Service` to avoid use-after-close

This commit delivers implementation of `onClose` calls ordering.

When instance factory creates its first instance, it means that
all dependencies are also created. So it's enough to sort factories
by 'first instance creation,' and then reverse the sequence when
invoking `onClose`.

Implementation:
  * global 'instance creation order' atomic counter is created
  * each instance factory has 'instance creation order' position
  * when factory creates its first instance
    * 'instance creation order' counter is incremented
    * the counter's value is saved into 'instance creation order'
      position of the factory
  * factories can be sorted by 'instance creation order' position,
    this can be used to order `onClose` calls
@@ -28,8 +30,12 @@ import org.koin.mp.Lockable
/**
* Koin Instance Holder
* create/get/release an instance of given definition
*
* Implements [Comparable] to provide factories soring by instance creation order
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: sorting


// it's required to have `Long.MAX_VALUE` instantiations happened to make this check fail,
// which is not likely to happen
check(instanceCreationOrderPosition < Long.MAX_VALUE) {
Copy link
Contributor

@marcellogalhardo marcellogalhardo Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instanceCreationOrderPosition default value is MAX_VALUE and this check throw an exception if it hit MAX_VALUE.

I'm not sure if I understand your code comment above and it seems error prone.

abstract class InstanceFactory<T>(val beanDefinition: BeanDefinition<T>) : Lockable() {
abstract class InstanceFactory<T>(val beanDefinition: BeanDefinition<T>) : Lockable(), Comparable<InstanceFactory<*>> {
private val instanceCreationOrderSet = AtomicBoolean(false)
private var instanceCreationOrderPosition = Long.MAX_VALUE
Copy link
Contributor

@marcellogalhardo marcellogalhardo Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current design ties a child instance natural comparison to its insertion ordering within a parent collection. A Comparable is documented as a way to define a class's natural order, not their insertion order. See Comparable and Natural Sort Order.

Instead, what if the InstanceRegistry would handle the instances collection and closing ordering?

Here's a possible approach:

  • The InstanceRegistry could use a ConcurrentLinkedDeque to store keys, ensuring thread-safe ordering of factory registrations.
  • The InstanceRegistry would then access the deque to order the factory instances and close them in the correct order.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thought here. Consider another proposal @shchuko

Copy link
Member

@arnaudgiuliani arnaudgiuliani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see for another solution 👍 See Marcello's comment

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

Successfully merging this pull request may close these issues.

Order of onClose calls
4 participants