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
1=== modified file 'storm/database.py'
2--- storm/database.py 2012-06-27 18:41:59 +0000
3+++ storm/database.py 2013-02-06 22:47:23 +0000
4@@ -369,13 +369,21 @@
5 @return: The dbapi cursor object, as fetched from L{build_raw_cursor}.
6 """
7 raw_cursor = self._check_disconnect(self.build_raw_cursor)
8- self._check_disconnect(
9- trace, "connection_raw_execute", self, raw_cursor,
10- statement, params or ())
11+ self._prepare_execution(raw_cursor, params, statement)
12+ args = self._execution_args(params, statement)
13+ self._run_execution(raw_cursor, args, params, statement)
14+ return raw_cursor
15+
16+ def _execution_args(self, params, statement):
17+ """Get the appropriate statement execution arguments."""
18 if params:
19 args = (statement, tuple(self.to_database(params)))
20 else:
21 args = (statement,)
22+ return args
23+
24+ def _run_execution(self, raw_cursor, args, params, statement):
25+ """Complete the statement execution, along with result reports."""
26 try:
27 self._check_disconnect(raw_cursor.execute, *args)
28 except Exception, error:
29@@ -387,7 +395,18 @@
30 self._check_disconnect(
31 trace, "connection_raw_execute_success", self, raw_cursor,
32 statement, params or ())
33- return raw_cursor
34+
35+ def _prepare_execution(self, raw_cursor, params, statement):
36+ """Prepare the statement execution to be run."""
37+ try:
38+ self._check_disconnect(
39+ trace, "connection_raw_execute", self, raw_cursor,
40+ statement, params or ())
41+ except Exception, error:
42+ self._check_disconnect(
43+ trace, "connection_raw_execute_error", self, raw_cursor,
44+ statement, params or (), error)
45+ raise
46
47 def _ensure_connected(self):
48 """Ensure that we are connected to the database.
49@@ -528,5 +547,5 @@
50 factory = module.create_from_uri
51 return factory(uri)
52
53-# Deal with circular import.
54+# Deal with circular import.
55 from storm.tracer import trace
56
57=== modified file 'tests/database.py'
58--- tests/database.py 2012-06-27 17:59:50 +0000
59+++ tests/database.py 2013-02-06 22:47:23 +0000
60@@ -229,6 +229,20 @@
61 ("ERROR", self.connection, RawCursor,
62 "something", (), error)])
63
64+ def test_raw_execute_setup_error_tracing(self):
65+ cursor_mock = self.mocker.patch(FakeTracer)
66+ cursor_mock.connection_raw_execute(ARGS)
67+ error = ZeroDivisionError()
68+ self.mocker.throw(error)
69+ self.mocker.replay()
70+
71+ tracer = FakeTracer()
72+ install_tracer(tracer)
73+ self.assertRaises(ZeroDivisionError,
74+ self.connection.execute, "something")
75+ self.assertEquals(tracer.seen, [("ERROR", self.connection, RawCursor,
76+ "something", (), error)])
77+
78 def test_tracing_check_disconnect(self):
79 tracer = FakeTracer()
80 tracer_mock = self.mocker.patch(tracer)

Subscribers

People subscribed via source and target branches

to status/vote changes: