-
Notifications
You must be signed in to change notification settings - Fork 54
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
feat(postgres): Adding a postgres async pool to make the db interactions asynchronous #1779
Conversation
# blocking the async runtime | ||
if pool.conns.anyIt(it.busy): | ||
while pool.conns.anyIt(it.busy): | ||
await sleepAsync(0.milliseconds) |
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.
Why is the non-sleeping sleep 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.
Or maybe a better question - what does it do?
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.
Hi @vpavlin ! The main purpose of this await
is to bring the Nim's asynchornous runtime the opportunity to attend other futures that are being "awaited". However, we might need to follow a better approach in further PR's (this comment is considered as a ToDo in the offending issue:)
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.
Great PR, left a few comments for style and couple queries, thanks :)
require: | ||
driverRes.isOk() | ||
let driver = driverRes.value | ||
let _ = await driver.reset() |
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.
let _ = await driver.reset() | |
discard await driver.reset() |
maybe?
|
||
require: | ||
driverRes.isOk() | ||
let driver = driverRes.value |
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.
let driver = driverRes.value | |
let driver = driverRes.get() |
require: | ||
not driver.isNil() | ||
let initRes = await driver.init() | ||
assert initRes.isOk(), initRes.error |
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.
Maybe we should follow this pattern in other test files too so we can see why the result is an error!
non-blocker
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.
Yes, I'll set a separate PR asap with 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.
Morning @rymnc ! After checking the code to apply this change I see that the change would be quite wide but we won't get very great investment return by doing so, as the tests don't tend to fail very often.
Therefore, I think is better to just apply this technique from now on in the upcoming tests.
Of course, if you consider it worth the effort, then I will make it for the current tests :)
chronos | ||
import | ||
../../driver, | ||
connection |
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.
should this import be with the std lib import group?
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 this refers to a module set in the same folder. I'll make it more explicit with ./connection
let conn = pool.conns[connIndexRes.value].dbConn | ||
defer: pool.releaseConn(conn) | ||
|
||
return await conn.rows(sql(query), args) |
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.
Maybe we should try-except here to bubble the error as a result instead of a failed future?
# Each session/connection should have its own prepared statements. | ||
try: | ||
pool.conns[connIndexRes.value].insertStmt = | ||
conn.prepare("insertRow", sql(baseStmt), 7) |
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'm probably missing something here, what is 7?
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.
7 is the number of parameters to be handled.
In this case, we are only setting an unique "insertRow" prepared statement and the baseStmt
has the next value:
INSERT INTO messages (id, storedAt, contentTopic, payload, pubsubTopic, version, timestamp) VALUES ($1, $2, $3, $4, $5, $6, $7);
The concept of "prepared statement" is interesting from a performance point of view.
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 see! thanks, its much clearer now
Can we make this value a constant near the "prepared statement", in case the value changes in the future?
proc open*(connString: string): | ||
Result[DbConn, string] = | ||
## Opens a new connection. | ||
var conn:DbConn = 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.
var conn:DbConn = nil | |
var conn: DbConn = nil |
return err("failed to reset database") | ||
except DbError: | ||
return err("failed to reset database") | ||
let ret = await s.connPool.exec(createTableQuery(), newSeq[string](0)) |
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.
try-except here and bubble the error within the result?
@@ -282,9 +280,9 @@ proc sleep*(s: PostgresDriver, seconds: int): | |||
# database for the amount of seconds given as a parameter. | |||
try: | |||
let params = @[$seconds] | |||
s.connection.exec(sql"SELECT pg_sleep(?)", params) | |||
let _ = await s.connPool.query("SELECT pg_sleep(?)", params) |
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.
let _ = await s.connPool.query("SELECT pg_sleep(?)", params) | |
asyncCheck await s.connPool.query("SELECT pg_sleep(?)", params) |
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.
https://forum.nim-lang.org/t/5279#33208
discard is OK for tests imo (in some situations)
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.
Thanks for the recommendation! However, the test got stuck with that.
Therefore, in the end I'll suggest sth like:
let sleepRes = await s.connPool.query("SELECT pg_sleep(?)", params)
if sleepRes.isErr():
return err("error in postgres_driver sleep: " & sleepRes.error)
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.
LGTM and thanks for taking this over!
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.
LGTM, awesome PR!
Summary
Allowing asynchronous calls
Further details
This PR:
How to test
Issue
#1604