Re-using ClientBootstrap… but why?

using swift on server is so, so frustrating because there is so little guidance out there that i never have any clue if i am doing the right thing or if i am going down the completely wrong path.

one thing i have been puzzling over is the recommendation to reuse ClientBootstrap across multiple connections:

Usually you re-use a ClientBootstrap once you set it up and called connect multiple times on it. This way you ensure that the same EventLoops will be shared across all your connections.

but if you reuse the same ClientBootstrap across all your connections, then all of the connections have to yield their results to the same AsyncStream.Continuation. for example:

    var source:AsyncStream<ResponseFacet>.Continuation!
    let stream:AsyncStream<ResponseFacet> = .init
    {
        source = $0
    }
    let bootstrap:ClientBootstrap = .init(group: threads)
        .connectTimeout(.seconds(3))
        .channelInitializer
    {
        //  create a handler that wraps the continuation and add it
        //  to the pipeline...
    }

so now we have one stream shared by many connections.

the thing is, i never have a need for more than one connection to exist at a time; batches of “client work” are processed in sequence, and within a single batch of work, parallelism is provided by the HTTP/2 layer. so reusing the same AsyncStream, which is an implication of reusing the same ClientBootstrap, does not make a lot of sense to me. logically, i feel like it would be more natural for each connection to have its own stream.

perhaps i am missing something big here. can someone explain why it is recommended to reuse the same ClientBootstrap for multiple connections?

As long as you share the same EventLoopGroup with different ClientBootstraps you will use the EventLoops from the same pool and you are fine. Nothing wrong with creating multiple ClientBoostraps as connection just calls next() on the EventLoopGroup anyway:

Note that channelInitializer may be called multiple times because we may try to race IPv4 and IPv6 connections simultaneously and setup a full channel for each.

1 Like

the ELG is part of the context i am already maintaining, so that’s not a problem. ClientBootstrap is a rather awkward type to wrap since it is non-Sendable, whereas (MultiThreaded)EventLoopGroup is Sendable.

it sounds to me like it would be a lot more sensible to create a new, locally-scoped ClientBootstrap for each “generation” of client connections. which kind of contradicts the guidance from those docs…

1 Like

As David says, don't worry about creating new ClientBootstraps if that's more convenient. Even if you wanted to specify a precise EventLoop that's not an issue because any EventLoop is also an EventLoopGroup (containing just itself).

I think the comment mostly wanted to say that it is possible to reuse ClientBootstraps and in other scenarios it is quite convenient as you don't have to configure it over and over again.

1 Like