PostgresClientKit icon indicating copy to clipboard operation
PostgresClientKit copied to clipboard

Please migrate from BlueSocket and BlueSSLService to swift-nio

Open sdpopov-keyvariable opened this issue 3 years ago • 8 comments

Migrate from BlueSocket and BlueSSLService to definitely preferred Apple's open source swift-nio.

Currently BlueSocket and BlueSSLService block modern versions of swift-argument-parser. They depend on 'swift-argument-parser' 0.4.1..<1.0.0. It's definitely wrong when networking libraries (not their applications) depend on argument parser.

sdpopov-keyvariable avatar Jul 16 '22 20:07 sdpopov-keyvariable

NIO has its own PG library (https://github.com/vapor/postgres-nio). Though PG access doesn't really make sense in a non-blocking context in the first place. Would be nice to keep that as a proper blocking lib.

helje5 avatar Jul 16 '22 20:07 helje5

Let's move the swift-argument-parser version constraint problem to #42.

And use this issue (#41) to explore the bigger question of moving PostgresClientKit from BlueSocket/BlueSSLService to swift-nio.

pitfield avatar Jul 16 '22 23:07 pitfield

(1) I'd also like to be rid of BlueSocket/BlueSSL in favor of Network Framework, which is already baked into macOS, iOS, watchOS, and tvOS. I've moved all my API services from Swift-NIO to NW-framework and not looking back. NIO is somewhat long in the tooth and I consider it bloatware at this point. WIth no async/await and now only Vapor as its chief supporter, one has to wonder how long NIO will be maintained in its current form.

(2) Speaking of async/await... Overall performance and ease-of-use would also be improved if PostgresClientKit moved to an async/await model instead of dispatch. Dispatch is not totally dead, but it's slow-walking toward the graveyard.

ebsanford avatar Feb 11 '23 16:02 ebsanford

I agree that removing the BlueSocket/BlueSSLService dependencies is highly desirable. Thank you for your thoughts about SwiftNIO vs. Network Framework. Right now, I don't know enough to have my own opinion on this. @helje5, you have written about this. Do you have any further guidance on selecting a framework for a non-HTTP TCP client?

I see how an async/await API would be helpful for ConnectionPool, replacing the completion handler pattern. Do you see benefit to async APIs elsewhere?

pitfield avatar Feb 15 '23 18:02 pitfield

(1) We're almost 3 hours into the change of timeout value back to 0, and the behavior has be much, much better. I’ll know more after 24 hours or so. But for now, it looks like the correct change. I guess somewhere along the way I simply missed the part where the workaround required the timeout to be 0. Most likely I read right over that tidbit and it simply bounced off my thick skull LOL!

(2) I originally wrote all my APIs in Swift using the NIO framework as the HTTP wrapper. NIO handles all the client-facing networking (i.e. certificates, https handshaking, etc.) and eventually hands all client request data to your API code. At this point the API does its thing and hands the results back to NIO, which runs it back down the output pipeline to the remote client. It works, It’s reliable, and it’s also large, complex, and full of it’s own concepts and paradigms.

But that was 2018-ish and NIO was the only thing for server-side Swift. NIO is reliable for sure, and it also has a Postgres-NIO add-on that is also reliable, but also equally difficult to implement. NIO is also thread-centric, and has its own promise/future/runloop mechanism that I could never fully grasp (thick skull syndrome again). After years of conditioning my brain to dispatch queues, the thread-centric model of NIO seemed very foreign to me. Most NIO programming for me consisted of copy/paste/pray. I can say I’ve never fully understood it.

Now, here we are in 2023 and Swift 5.7 is well down its own runtime concurrency path. This model is something completely different from NIO’s promise/future system, and has analogs back to dispatch. I suspect that async/await/actors are built on top of dispatch, or at least a framework that’s both dispatch-aware and dispatch-cooperative. That, along with the fact that this new model has once again booted explicit threads and runloops out of the programming vernacular, has really benefited my mental picture of how it’s working under the hood. As for the HTTP wrapper part, I’m using Apple’s native Network Framework, which they have put forth as an IETF standard. In fact, NIO will run on top of Network Framework if it's running on an Apple device. Network Framework is not tied to any particular protocol, but operates at the TCP/UDP level and should easily handle persistent TCP/IP connections. So I’ve had to implement my own HTTP server protocol on top of a Network Framework listener object.

The big benefit I see from async/await is that it’s effectively a runtime sub-thread scheduling system. No longer does the entire thread have to context switched to/from a CPU core. In stead, a thread can stay in context on the CPU to which it is assigned, and the Swift runtime uses its internal trickery to do on-thread context switching of just the code blocks and stack segments that need switching. I’m not sure how it all works, but my API is now at least 15% faster on the same CPU. I also suspect that async/await will allow future versions of Swift to generate more and more efficient scheduling code for ARM cores. Since async/await is embedded in the programmer’s code, this optimization can be become very specific to the function and code block at hand.

Async/await will definitely improve overall program flow as it eliminates virtually all closures. In addition, rewriting your connection pool as a singleton Actor will eliminate virtually all the code you have tied up in dispatch queue management and avoiding race conditions.

—Barry

On Feb 15, 2023, at 12:38 PM, David Pitfield [dbp] @.***> wrote:

I agree that removing the BlueSocket/BlueSSLService dependencies is highly desirable. Thank you for your thoughts about SwiftNIO vs. Network Framework. Right now, I don't know enough to have my own opinion on this. @helje5 https://github.com/helje5, you have written https://www.alwaysrightinstitute.com/network-framework/ about this. Do you have any further guidance on selecting a framework for a non-HTTP TCP client?

I see how an async/await API would be helpful for ConnectionPool, replacing the completion handler pattern. Do you see benefit to async APIs elsewhere?

— Reply to this email directly, view it on GitHub https://github.com/codewinsdotcom/PostgresClientKit/issues/41#issuecomment-1431835389, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABTRL5C5QRQBO6SIDCLEBJLWXUPD5ANCNFSM53YSZUAQ. You are receiving this because you commented.

ebsanford avatar Feb 15 '23 21:02 ebsanford

  1. Thanks for the update. Please see my comment over at #10.

  2. Thanks for sharing your experience and thoughts on Swift NIO & Network Framework. Appreciated!

pitfield avatar Feb 16 '23 16:02 pitfield

Thanks, David, for your expeditious assistance. I went back and re-read the API docs. And sure enough, for socketTimeout: "The timeout for socket operations, in seconds, or 0 for no timeout." In my mind, I was mistakenly reading this as "The timeout for the socket to complete the connection." It's much clearer to me now that this means ALL socket operations for a given connection, including wait time for the remote server to respond.

My initial 10-second value wasn't causing a problem at first. But as my API service has become more and more busy, this value was not allowing enough time between the Execute statement and when a loaded-down, remote server could finally respond. I wasn't comfortable with a "never timeout." So I've set the production value to 300 and it seems to be humming along in splendid fashion, even in SSL mode. More CPUs in the AWS config are certainly on the docket as well.

ebsanford avatar Feb 16 '23 20:02 ebsanford