typelevel / typelevel/skunk

Inefficient use of Flush in extended query protocol

Open
#605 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Scala
Stars
1.7k
Forks
175
Avg merge
5d 14h
Merged PRs (30d)
9

Description

When executing a prepared statement, Skunk currently uses the following protocol flow:

  1. Parse
  2. Flush
  3. Await response and check for errors
  4. Describe statement
  5. Flush
  6. Await response and check for errors
  7. Bind
  8. Flush
  9. Await response and check for errors
  10. Execute
  11. Flush
  12. Await response and check for errors
  13. Close portal
  14. Flush
  15. Await response and check for errors
  16. Close statement
  17. Flush
  18. Await response and check for errors

This is needlessly inefficient, as it prevents batching of request commands and requires a full network round-trip on every step.

In addition, this currently fails to work—and instead hangs indefinitely—with a Pgpool-II server, which does not fully honour Flush. (This is currently being discussed on the pgpool-general mailing list.)

As far as I can tell, the only reason it is done this way is that MessageSocket does not allow queueing up response processing asynchronously and submitting more commands in the meantime. For example, skunk.net.protocol.Parse currently does this:

https://github.com/tpolecat/skunk/blob/9e0744d00f6876f1eda5016ea34d7f0ecac40736/modules/core/shared/src/main/scala/net/protocol/Parse.scala#L38-L55

where flatExpect suspends the fibre handling the current database connection until a response message is received. In an ideal world, it would schedule the block to be run whenever the next message arrives, but continue executing the sending fibre so more protocol commands can be queued; so the Flush could be omitted.

Any idea as to how this could be allowed in a clean way? I’m thinking that the sending and receiving should be separate fibres, but I’m not well-versed enough to see immediately how to achieve that.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked Parse.scala section and inspect MessageSocket, especially how flatExpect suspends response handling. Trace the prepared-statement protocol flow and determine how request batching can avoid per-step Flush calls while still processing responses; done includes avoiding the Pgpool-II hang.

Written by the indexing model from the issue text.

Assessment

Tech stack
postgres, scala
Domain
databases, networking
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.