-
Notifications
You must be signed in to change notification settings - Fork 18
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
Async IO with CLibvenice #15
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great! Sorry took so long to review. The PR needs a bit of cleanup (not a problem), but I'm not 100% certain the semantics are correct. More in the next comment.
case PGRES_POLLING_OK: | ||
break loop | ||
case PGRES_POLLING_READING: | ||
mill_fdwait_(fd, FDW_IN, 15.seconds.fromNow().int64milliseconds, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use Venice.poll
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Venice.poll doesn't clean the file descriptor when it's done
mill_fdwait_(fd, FDW_IN, 15.seconds.fromNow().int64milliseconds, nil) | ||
mill_fdclean_(fd) | ||
case PGRES_POLLING_WRITING: | ||
mill_fdwait_(fd, FDW_OUT, 15.seconds.fromNow().int64milliseconds, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Venice.poll doesn't clean the file descriptor when it's done
import CLibpq | ||
import CLibvenice |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CLibvenice -> Venice - I don't think we're using any api's that venice doesn't wrap (if we are, should use both?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does Venice have an API to explicitly clean up a fdwait? We have to wait and then immediately clean it up otherwise when libpq goes to read it causes a crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, doesn't look like it. Venice.poll just wraps fdwait
- I guess we can use fdclean
from CLibvenice.
return try Result(result) | ||
extension Collection where Iterator.Element == String { | ||
|
||
func withUnsafeCStringArray<T>(_ body: (UnsafePointer<UnsafePointer<Int8>?>) throws -> T) rethrows -> T { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you forgot to use this :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha yes! I need to get rid of it 👍
throw ConnectionError(description: "Connection already opened.") | ||
} | ||
|
||
var components = URLComponents() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, better style would be
let url: String = {
var components = URLComponents()
// ...
return components.url!.absoluteString
}()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally just think that is a nit, it's a local scope var and mutated either way step by step…
throw mostRecentError ?? ConnectionError(description: "Could not get file descriptor.") | ||
} | ||
|
||
loop: while true { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loop isn't nested, so I don't think a label is necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I can get rid of that loop label
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually you can't break from a switch to break the loop, hence the label?
mill_fdwait_(fd, FDW_OUT, 15.seconds.fromNow().int64milliseconds, nil) | ||
mill_fdclean_(fd) | ||
case PGRES_POLLING_ACTIVE: | ||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continue
instead of break
to avoid the label. This might be "clever", though...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only if the loop label can be fixed
var parameterData = [UnsafePointer<Int8>?]() | ||
var deallocators = [() -> ()]() | ||
defer { deallocators.forEach { $0() } } | ||
|
||
for parameter in parameters { | ||
if let parameters = parameters { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function got really long - needs some cleaning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly, it works though? It's still pretty small
do { | ||
connection = try! PostgreSQL.Connection(info: .init(URL(string: "postgres://localhost:5432/swift_test")!)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason the connection is being reconstructed each time? Is this the right way to use the api now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For tests each test should use it's own connection, we shouldn't share it due to busy states/dangling etc…
Connections are also unrecoverable ( you can't "reconnect" )
Two things lead me to think that we're not polling quite right:
without this pr:
with this pr:
|
Also, it didn't actually build for me (3.0.2). Are you using a modified version of libmill/venice? |
@Danappelxx as per the performance your tests are re-using a connection I assume? This is why it appears to perform slower. Before a single request will execute the full SQL command before any other request in the entire process could continue thus you sort of have an implicit lock on any SQL execution. Now when libpq is waiting for IO another coroutine will pick up a new request and start to try and use the same Postgres connection, Postgres is half way through executing another SQL statement and hence you get the errors. For this async stuff to work we really need a connection pool that a request checks a connection out and when finished it puts it back. |
Did some more testing and I'm satisfied! Works great with a connection pool as you suggested. Just a little bit of cleanup and its good to go. |
(merge whenever you're ready) |
This PR makes PostgreSQL non-blocking and play nice with Libvenice:
@Danappelxx please review 👍