Skip to content

Make execution of plpython interruptible #5

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

Closed
wants to merge 1 commit into from

Conversation

jgoizueta
Copy link

When a postgres process receives a SIGINT, the default behaviour (handled by the function StatementCancelHandle) it to set up a couple of flags to indicate the occurrence of the interrupt and hopefully cancel ongoing queries.

Since execution of PLPython functions does not check those flags, the execution of long-running code may go uninterrupted.

This was discussed here.

This patch makes execution of PLPython interruptible by hooking into the PostgreSQL signal-handling mechanism with pqsignal (like for example PostGIS also does) and inserting a pending call into the Python VM to generate a Python exception.

Execution could still go uninterrupted if for example long-running external code is called.

Acknowledgements

It was @ethervoid who initially investigated this problem and designed this solution.

Notes on the implementation

Care is taken to avoid inserting or executing python interruptions outside of the proper PLPython contexts. (plpython contexts can be nested as plpython functions are executed through SPI).

  • If PLPython is not being executed when the signal is received no interruption is inserted
  • If the top level plpython context when the signal is received finishes, pending interruptions are ignored.

Thus we prevent interruptions being effective in later, unrelated queries or commands.

Note also that the python pending call handler must return -1 when an interruption is generated;
failing to do so would cause the interruption to be queued for later execution at an indeterminate time.

The plpy module hooks into the signal handling mechanism to insert python exceptions when a SIGINT occurs.
@@ -74,13 +74,19 @@ PyObject *PLy_interp_globals = NULL;
/* this doesn't need to be global; use PLy_current_execution_context() */
static PLyExecutionContext *PLy_execution_contexts = NULL;

+/* postgres backend handler for interruption */
Copy link

@ethervoid ethervoid May 16, 2017

Choose a reason for hiding this comment

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

You left some + symbols from the diff that makes this file fail at compilation time

Copy link
Author

Choose a reason for hiding this comment

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

oops! fixing it, thanks!

Py_AddPendingCall(PLy_python_interruption_handler, NULL);
}
// Fallback to execute prior handlers
if (coreIntHandler != SIG_DFL && coreIntHandler != SIG_IGN) {

Choose a reason for hiding this comment

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

SIG_DFL act as a process kill so better to have it out of here :)

static int
PLy_python_interruption_handler()
{
if (!PLy_pending_interrupt) {
Copy link

@ethervoid ethervoid May 17, 2017

Choose a reason for hiding this comment

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

Is this possible? or we're making defensive programming just in case?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, it will happen when the top PlPython context exits and there are unhandled Python interruption; since we can't remove the pending call we set this flag to 0 to make it have no effect.

Copy link

@ethervoid ethervoid left a comment

Choose a reason for hiding this comment

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

Despite some comments, the PR looks nice to me. Good job and great improvement kudos!

@ethervoid
Copy link

I'm going to need some acceptance steps. I know you worked on some acceptance tests with multiple sessions and pgbouncer :)

@rafatower
Copy link

Continues on #7

@rafatower rafatower closed this May 23, 2017
rafatower pushed a commit to CartoDB/postgresql-debian that referenced this pull request May 23, 2017
See CartoDB/postgres#5

and more specifically the commit b319cee
Algunenano pushed a commit that referenced this pull request Nov 30, 2018
refresh_by_match_merge() has some issues in the way it builds a SQL
query to construct the "diff" table:

1. It doesn't require the selected unique index(es) to be indimmediate.
2. It doesn't pay attention to the particular equality semantics enforced
by a given index, but just assumes that they must be those of the column
datatype's default btree opclass.
3. It doesn't check that the indexes are btrees.
4. It's insufficiently careful to ensure that the parser will pick the
intended operator when parsing the query.  (This would have been a
security bug before CVE-2018-1058.)
5. It's not careful about indexes on system columns.

The way to fix #4 is to make use of the existing code in ri_triggers.c
for generating an arbitrary binary operator clause.  I chose to move
that to ruleutils.c, since that seems a more reasonable place to be
exporting such functionality from than ri_triggers.c.

While #1, #3, and #5 are just latent given existing feature restrictions,
and #2 doesn't arise in the core system for lack of alternate opclasses
with different equality behaviors, #4 seems like an issue worth
back-patching.  That's the bulk of the change anyway, so just back-patch
the whole thing to 9.4 where this code was introduced.

Discussion: https://postgr.es/m/13836.1521413227@sss.pgh.pa.us
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.

3 participants