Skip to content
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

Merged
merged 10 commits into from
Jun 7, 2023

Conversation

Ivansete-status
Copy link
Collaborator

Summary

Allowing asynchronous calls

Further details

This PR:

How to test

  1. Start postgres database locally
    sudo docker compose -f tests/docker-compose.yml up
    
  2. Run tests locally
    . env.sh
    
    nim c -r -d:chronicles_log_level=WARN --verbosity=0 --hints=off --outdir=build ./tests/v2/waku_archive/test_driver_postgres.nim
    

Issue

#1604

# blocking the async runtime
if pool.conns.anyIt(it.busy):
while pool.conns.anyIt(it.busy):
await sleepAsync(0.milliseconds)
Copy link
Member

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?

Copy link
Member

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?

Copy link
Collaborator Author

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:)

#1631 (comment)

Copy link
Contributor

@rymnc rymnc left a 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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let _ = await driver.reset()
discard await driver.reset()

maybe?


require:
driverRes.isOk()
let driver = driverRes.value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let driver = driverRes.value
let driver = driverRes.get()

require:
not driver.isNil()
let initRes = await driver.init()
assert initRes.isOk(), initRes.error
Copy link
Contributor

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

Copy link
Collaborator Author

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

Copy link
Collaborator Author

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
Copy link
Contributor

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?

Copy link
Collaborator Author

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)
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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))
Copy link
Contributor

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let _ = await s.connPool.query("SELECT pg_sleep(?)", params)
asyncCheck await s.connPool.query("SELECT pg_sleep(?)", params)

Copy link
Contributor

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)

Copy link
Collaborator Author

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)

Copy link
Contributor

@jm-clius jm-clius left a 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!

Copy link
Contributor

@rymnc rymnc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, awesome PR!

@Ivansete-status Ivansete-status merged commit cb2e3d8 into master Jun 7, 2023
13 checks passed
@Ivansete-status Ivansete-status deleted the feat-1604-pgconn-async-pool branch June 7, 2023 08:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants