Merge lp:~diogobaeder/storm/cleanup-timeline into lp:storm

Proposed by Sidnei da Silva
Status: Merged
Merged at revision: 456
Proposed branch: lp:~diogobaeder/storm/cleanup-timeline
Merge into: lp:storm
Diff against target: 80 lines (+38/-5)
2 files modified
storm/database.py (+24/-5)
tests/database.py (+14/-0)
To merge this branch: bzr merge lp:~diogobaeder/storm/cleanup-timeline
Reviewer Review Type Date Requested Status
Free Ekanayaka (community) Approve
Review via email: mp+146980@code.launchpad.net

Description of the change

Make sure connection_raw_execute_error is called when the tracer's connection_raw_execute fails, so that the tracers have a chance to clean themselves up.

To post a comment you must log in.
Revision history for this message
James Westby (james-w) wrote :

I've just been discussing this changes with Diogo and Sidnei, and now understand something
I missed first time. This change is most important when using two tracers:

   A -> connection_raw_execute -> success
   B -> connection_raw_execute -> exception

A is then left in a state where it doesn't know that the execution was halted, which
this patch corrects.

Thanks,

James

Revision history for this message
Sidnei da Silva (sidnei) wrote :

There's an alternative failure scenario that this fix doesn't cover:

  - If the cursor.execute() raises a DatabaseError, the PostgresTimeoutTracer's connection_raw_execute_error may turn that into a TimeoutError and re-raise.
  - If the TimelineTracer (or any other tracer for that matter) comes last, then it's connection_raw_execute_error will never be called because the exception bubbles up from trace(), never notifying the remaining tracers.

An additional solution to that problem might be to also make the trace() function more robust, ensuring that it always goes through all the tracers and collects all exceptions, raising some sort of MultiException if there is more than one.

The change of behaviour *could* break people relying on the broken behaviour (eg, expecting that if one of the tracers fails, the remaining ones will never execute), but I don't think anyone in their sane mind would do that.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Looks good, +1!

[1]

+ def test_raw_execute_setup_error_tracing(self):

Please add a docstring for this test. I know it's not there in every old test, but we're trying to do that for new tests.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'storm/database.py'
--- storm/database.py 2012-06-27 18:41:59 +0000
+++ storm/database.py 2013-02-06 22:47:23 +0000
@@ -369,13 +369,21 @@
369 @return: The dbapi cursor object, as fetched from L{build_raw_cursor}.369 @return: The dbapi cursor object, as fetched from L{build_raw_cursor}.
370 """370 """
371 raw_cursor = self._check_disconnect(self.build_raw_cursor)371 raw_cursor = self._check_disconnect(self.build_raw_cursor)
372 self._check_disconnect(372 self._prepare_execution(raw_cursor, params, statement)
373 trace, "connection_raw_execute", self, raw_cursor,373 args = self._execution_args(params, statement)
374 statement, params or ())374 self._run_execution(raw_cursor, args, params, statement)
375 return raw_cursor
376
377 def _execution_args(self, params, statement):
378 """Get the appropriate statement execution arguments."""
375 if params:379 if params:
376 args = (statement, tuple(self.to_database(params)))380 args = (statement, tuple(self.to_database(params)))
377 else:381 else:
378 args = (statement,)382 args = (statement,)
383 return args
384
385 def _run_execution(self, raw_cursor, args, params, statement):
386 """Complete the statement execution, along with result reports."""
379 try:387 try:
380 self._check_disconnect(raw_cursor.execute, *args)388 self._check_disconnect(raw_cursor.execute, *args)
381 except Exception, error:389 except Exception, error:
@@ -387,7 +395,18 @@
387 self._check_disconnect(395 self._check_disconnect(
388 trace, "connection_raw_execute_success", self, raw_cursor,396 trace, "connection_raw_execute_success", self, raw_cursor,
389 statement, params or ())397 statement, params or ())
390 return raw_cursor398
399 def _prepare_execution(self, raw_cursor, params, statement):
400 """Prepare the statement execution to be run."""
401 try:
402 self._check_disconnect(
403 trace, "connection_raw_execute", self, raw_cursor,
404 statement, params or ())
405 except Exception, error:
406 self._check_disconnect(
407 trace, "connection_raw_execute_error", self, raw_cursor,
408 statement, params or (), error)
409 raise
391410
392 def _ensure_connected(self):411 def _ensure_connected(self):
393 """Ensure that we are connected to the database.412 """Ensure that we are connected to the database.
@@ -528,5 +547,5 @@
528 factory = module.create_from_uri547 factory = module.create_from_uri
529 return factory(uri)548 return factory(uri)
530549
531# Deal with circular import. 550# Deal with circular import.
532from storm.tracer import trace551from storm.tracer import trace
533552
=== modified file 'tests/database.py'
--- tests/database.py 2012-06-27 17:59:50 +0000
+++ tests/database.py 2013-02-06 22:47:23 +0000
@@ -229,6 +229,20 @@
229 ("ERROR", self.connection, RawCursor,229 ("ERROR", self.connection, RawCursor,
230 "something", (), error)])230 "something", (), error)])
231231
232 def test_raw_execute_setup_error_tracing(self):
233 cursor_mock = self.mocker.patch(FakeTracer)
234 cursor_mock.connection_raw_execute(ARGS)
235 error = ZeroDivisionError()
236 self.mocker.throw(error)
237 self.mocker.replay()
238
239 tracer = FakeTracer()
240 install_tracer(tracer)
241 self.assertRaises(ZeroDivisionError,
242 self.connection.execute, "something")
243 self.assertEquals(tracer.seen, [("ERROR", self.connection, RawCursor,
244 "something", (), error)])
245
232 def test_tracing_check_disconnect(self):246 def test_tracing_check_disconnect(self):
233 tracer = FakeTracer()247 tracer = FakeTracer()
234 tracer_mock = self.mocker.patch(tracer)248 tracer_mock = self.mocker.patch(tracer)

Subscribers

People subscribed via source and target branches

to status/vote changes: