Skip to content

Solve the prepared statement automatic-release difficulty #159

@Abscissa

Description

@Abscissa

Note: This is not an especially high priority unless someone discovers a real need to release prepared statements from the server. Since the server treats prepared statements as being specific to a connection (and connections eventually end), this seems unlikely.


Suppose we want to use automatic resource management (ie, a scoped/RAII/refcounted/etc. wrapper) for Prepared to automatically release the statement from a connection when it's done being used (again, AFAIK, this shouldn't be necessary, but suppose we want to do it). This poses a problem:

What if the prepared statement becomes ready for release while there is already data pending on the connection? That is, while a result set input range has not yet been completely read. (This has been demonstrated as a realistic possibility, by @schveiguy.)

Commands cannot be sent to the server while data is pending for retreival. The usual solution mysql-native takes (as of v1.1.4) is to auto-purge any pending data when a command needs to be issued. For the most part, this has worked out well, because:

  1. Issuing a command is explicit in user code, and
  2. Attempting to read from a resultset which has been purged results in an exception, not a surprise end-of-data.

But the problem is, if we use automatic resource management to release a prepared statement, then issuing the command "release this prepared statement" becomes implicit and hidden. In situations such as the demonstration mentioned above, this hidden auto-purge would cause surprising problems that can be difficult or messy to deal with.

In an attempt to avoid this problem, ever since v1.1.4, mysql-native has implemented a "queued for release" system. When mysql-native is told to release a prepared statement, it doesn't release it immediately. Instead, it saves it in a queue (but mysql-native still treats it as having been released, from the user code's perspecive). Then, mysql-native waits until the next time an explicit command is issued. At that point, any pending data is auto-purged and any prepared statements queued for release are finally released for real.

Unfortunately, this create a new problem when the cleanup happens to be triggered during a GC cycle. We don't know ahead of time how many prepared statements might need to be released. So queuing one for release involves appending an item (ie which statement to release) to an array. This may trigger an allocation, but...

Allocations cannot be performed during garbage collection (it will throw an InvalidMemoryOperationError). If the cleanup (and thus, queueing it for release) happens to be triggered during a GC cycle, boom: InvalidMemoryOperationError.

So this cycle needs to be broken. I'm hesistant to hardcode the size of the "to be released" queue, because for anything short of soaking up several gigabytes (ie, not realistic) it still leaves the difficult question of: What should happen when the hardcoded limit's exceeded? I'm not sure I like any of the possible ways to answer that question.


I was just going to post this as a "to be mulled over", but in the process of writing this I think I may have actually come up with a solution:

As long as we never queue duplicates, nor statements that haven't been registered on the given connection, then we're quaranteed the release queue will never exceed the number of statements actually registered. So if the queue is resized upon registration of prepared statements, then it should never need to allocate when queueing for release. Thus, marking it @nogc should be possible, thereby enabling safe, reliable automatic cleanup. Also, switching the array to an AA should prevent O(n) complexity when checking the queue for duplicates.(no good: "indexing an associative array...may cause GC allocation")

Hooray for pausing to take a step back.

Have I missed anything?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions