Skip to content

Implement Closeable in FunctionInstanceInjector interface and call close on JVM shutdown #812

Open
@ScottHamper

Description

@ScottHamper

Azure Functions Java supports dependency injection by implementing com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector and registering the implementation via SPI. However, when the DI container creates resources that need to be closed (via the Closeable interface), those resources will never have their close method called, as Azure Functions and the FunctionInstanceInjector interface provide no way of doing so. Similarly, there does not appear to be any way of closing resources on a graceful shutdown of the Azure Functions runtime/host (See #113, #729).

Official documentation states:

To avoid holding more connections than necessary, reuse client instances rather than creating new ones with each function invocation. We recommend reusing client connections for any language that you might write your function in.

And:

Do not create a new client with every function invocation.

Great - I would like to have my DI container hold a singleton for various clients (e.g., a Ktor HttpClient, or a database client that uses a HikariCP connection pool) and then have function invocations retrieve those clients from the container. Unfortunately, it does not currently seem possible to ensure these resources close properly.

Proposed Solution

Update FunctionInstanceInjector to implement Closeable, and update the Azure Functions runtime to call the close method when the runtime/host shuts down. The AF runtime is currently responsible for creating an instance of FunctionInstanceInjector, so it feels appropriate that it should be responsible for disposing of it.

Here's an example of what I'd like to be able to do (in Kotlin, using the Koin DI library):

abstract class KoinFunctionInstanceInjector(
    appDeclaration: KoinApplication.() -> Unit,
) : FunctionInstanceInjector {
    private val application = koinApplication(appDeclaration)

    override fun <T : Any> getInstance(clazz: Class<T>): T = application.koin.get(clazz.kotlin)

    // Currently a compilation error, because `FunctionInstanceInjector` does not implement `Closeable`
    override fun close() = application.close()
}

Then define a concrete class like:

class MyFunctionInstanceInjector :
    KoinFunctionInstanceInjector({
        modules(
            module {
                single { OkHttp.create() } onClose { it?.close() } bind HttpClientEngine::class
                single { HikariDataSource(/*...*/) } onClose { it?.close() } bind DataSource::class
                // ...
            },
        )
    })

And registering MyFunctionInstanceInjector via SPI. Calling application.close in KoinFunctionInstanceInjector ends up calling the onClose lambda for any/all instance definitions in the Koin module, allowing the HttpClientEngine and HikariDataSource instances to be closed properly.

The only thing missing is ensuring that the MyFunctionInstanceInjector instance that AF creates via SPI will have its close method called.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions