[Osaas-commits] r63 - in trunk: . server/osaas server/test

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Thu Jan 14 14:48:51 CET 2010


Author: bh
Date: 2010-01-14 14:48:51 +0100 (Thu, 14 Jan 2010)
New Revision: 63

Modified:
   trunk/ChangeLog
   trunk/server/osaas/dbbackend.py
   trunk/server/test/test_dbbackend.py
Log:
Make error handling in the db backend more robust.  This should
fix the main problem of geospatial/issue30


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2010-01-14 10:54:00 UTC (rev 62)
+++ trunk/ChangeLog	2010-01-14 13:48:51 UTC (rev 63)
@@ -1,5 +1,32 @@
 2010-01-14  Bernhard Herzog  <bh at intevation.de>
 
+	Make error handling in the db backend more robust.  This should
+	fix the main problem of geospatial/issue30
+
+	* server/osaas/dbbackend.py (DBBackend.log_exception): New method
+	to log exceptions analogous to the other log_* methods.
+	(DBBackend.ensure_connection): Add parameter force_new_connection
+	that can be used to force opening a new database connection
+	instead of reusing the existing one
+	(DBBackend.handle_record)
+	(DBBackend.insert_record_with_reconnect)
+	(DBBackend.insert_record): Split handle_record into handle_record,
+	insert_record_with_reconnect and insert_record.  handle_record
+	itself is the main entry point for the database insertion thread
+	and does the high level error handling, i.e. logging the record
+	contents if insertion fails for whatever reason.  insert_record
+	does the actual insertion and insert_record_with_reconnect calls
+	insert_record but retries if the database connection has been
+	closed unexpectedly (e.g. by a database server problem).  It also
+	forces new connections after each exception raised during the
+	insertion to make sure the connection object is in a usable state
+	for the next record.
+
+	* server/test/test_dbbackend.py (MockDbApi.OperationalError): Add
+	this class because it's used now by the dbbackend module.
+
+2010-01-14  Bernhard Herzog  <bh at intevation.de>
+
 	* server/osaas/dbbackend.py (DBBackend.get_insert_statement): New
 	method to generate and cache the insert statement for records.
 	(DBBackend.handle_record): Use the new method to get the insert

Modified: trunk/server/osaas/dbbackend.py
===================================================================
--- trunk/server/osaas/dbbackend.py	2010-01-14 10:54:00 UTC (rev 62)
+++ trunk/server/osaas/dbbackend.py	2010-01-14 13:48:51 UTC (rev 63)
@@ -78,7 +78,18 @@
         if self.logger is not None:
             self.logger.error(message, *args)
 
-    def ensure_connection(self):
+    def log_exception(self, message, *args):
+        if self.logger is not None:
+            self.logger.exception(message, *args)
+
+    def ensure_connection(self, force_new_connection=False):
+        if force_new_connection and self.connection is not None:
+            try:
+                self.connection.close()
+            except:
+                self.log_exception("Exception during connection.close()"
+                                   " while forcing a new connection")
+            self.connection = None
         if self.connection is None:
             self.log_debug("Connecting to database")
             self.connection = self.connect(*self.connection_parameters)
@@ -111,6 +122,39 @@
         return self.insert_statement
 
     def handle_record(self, record):
+        """Write record into the database.  Log the record if unsuccessful."""
+        try:
+            self.insert_record_with_reconnect(record)
+        except:
+            self.log_error("could not write record %s", record.raw_record)
+            raise
+
+    def insert_record_with_reconnect(self, record):
+        """Insert record into the database, reconnecting on some errors"""
+        try:
+            try:
+                self.insert_record(record)
+            except self.db_api.OperationalError:
+                self.log_exception("Got OperationalError."
+                                   " Trying again with a new connection"
+                                   " in case it's because of a DB server"
+                                   " connection problem")
+                self.ensure_connection(force_new_connection=True)
+                self.insert_record(record)
+        except:
+            # make sure to always use a new connection after an error
+            # happened.  If the network connection to the database
+            # vanishes unexpectedly, the DB API implementation might be
+            # left in an unusable state.  Also, we cannot just do this
+            # when an OperationalError is raised because some DB API
+            # implementations may raise different exceptions and even
+            # conforming implementations may raise other errors if the
+            # implementation
+            self.connection = None
+            raise
+
+    def insert_record(self, record):
+        """Insert record into the database."""
         statement = self.get_insert_statement()
         values = {}
         for f in self.rules[0].fields:
@@ -121,7 +165,6 @@
         try:
             cursor.execute(statement, values)
         except:
-            self.log_error("could not write record %s", record.raw_record)
             self.connection.rollback()
             raise
         else:

Modified: trunk/server/test/test_dbbackend.py
===================================================================
--- trunk/server/test/test_dbbackend.py	2010-01-14 10:54:00 UTC (rev 62)
+++ trunk/server/test/test_dbbackend.py	2010-01-14 13:48:51 UTC (rev 63)
@@ -1,4 +1,4 @@
-# Copyright (C) 2007, 2008 by Intevation GmbH
+# Copyright (C) 2007, 2008, 2010 by Intevation GmbH
 # Authors:
 # Bernhard Herzog <bh at intevation.de>
 #
@@ -55,7 +55,10 @@
 
     paramstyle = "named"
 
+    class OperationalError(Exception):
+        pass
 
+
 class MockLogger(object):
 
     def __init__(self):



More information about the Osaas-commits mailing list