From patchwork Wed Nov 2 20:56:13 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael-Doyle Hudson X-Patchwork-Id: 4926 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 4F19523DEE for ; Wed, 2 Nov 2011 20:56:16 +0000 (UTC) Received: from mail-fx0-f52.google.com (mail-fx0-f52.google.com [209.85.161.52]) by fiordland.canonical.com (Postfix) with ESMTP id 3877AA18262 for ; Wed, 2 Nov 2011 20:56:16 +0000 (UTC) Received: by faan26 with SMTP id n26so1232688faa.11 for ; Wed, 02 Nov 2011 13:56:16 -0700 (PDT) Received: by 10.223.6.129 with SMTP id 1mr374190faz.17.1320267375936; Wed, 02 Nov 2011 13:56:15 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.152.14.103 with SMTP id o7cs64644lac; Wed, 2 Nov 2011 13:56:15 -0700 (PDT) Received: by 10.216.138.209 with SMTP id a59mr1821077wej.94.1320267374057; Wed, 02 Nov 2011 13:56:14 -0700 (PDT) Received: from indium.canonical.com (indium.canonical.com. [91.189.90.7]) by mx.google.com with ESMTPS id t18si2950570wec.89.2011.11.02.13.56.13 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 02 Nov 2011 13:56:14 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) client-ip=91.189.90.7; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of bounces@canonical.com designates 91.189.90.7 as permitted sender) smtp.mail=bounces@canonical.com Received: from ackee.canonical.com ([91.189.89.26]) by indium.canonical.com with esmtp (Exim 4.71 #1 (Debian)) id 1RLhr3-0000VT-E8 for ; Wed, 02 Nov 2011 20:56:13 +0000 Received: from ackee.canonical.com (localhost [127.0.0.1]) by ackee.canonical.com (Postfix) with ESMTP id 5AAADE04DF for ; Wed, 2 Nov 2011 20:56:13 +0000 (UTC) MIME-Version: 1.0 X-Launchpad-Project: lava-scheduler X-Launchpad-Branch: ~linaro-validation/lava-scheduler/trunk X-Launchpad-Message-Rationale: Subscriber X-Launchpad-Branch-Revision-Number: 89 X-Launchpad-Notification-Type: branch-revision To: Linaro Patch Tracker From: noreply@launchpad.net Subject: [Branch ~linaro-validation/lava-scheduler/trunk] Rev 89: make sure all the transactions DatabaseJobSource opens are closed promptly Message-Id: <20111102205613.17608.16327.launchpad@ackee.canonical.com> Date: Wed, 02 Nov 2011 20:56:13 -0000 Reply-To: noreply@launchpad.net Sender: bounces@canonical.com Errors-To: bounces@canonical.com Precedence: bulk X-Generated-By: Launchpad (canonical.com); Revision="14214"; Instance="launchpad-lazr.conf" X-Launchpad-Hash: 3c63b54d380808d8bc89378b99260d8165774196 Merge authors: Michael Hudson-Doyle (mwhudson) Related merge proposals: https://code.launchpad.net/~mwhudson/lava-scheduler/dedangle-the-transaction/+merge/80651 proposed by: Michael Hudson-Doyle (mwhudson) ------------------------------------------------------------ revno: 89 [merge] committer: Michael Hudson-Doyle branch nick: trunk timestamp: Wed 2011-11-02 16:54:51 -0400 message: make sure all the transactions DatabaseJobSource opens are closed promptly modified: lava_scheduler_daemon/dbjobsource.py --- lp:lava-scheduler https://code.launchpad.net/~linaro-validation/lava-scheduler/trunk You are subscribed to branch lp:lava-scheduler. To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-scheduler/trunk/+edit-subscription === modified file 'lava_scheduler_daemon/dbjobsource.py' --- lava_scheduler_daemon/dbjobsource.py 2011-10-19 01:40:01 +0000 +++ lava_scheduler_daemon/dbjobsource.py 2011-10-28 05:36:02 +0000 @@ -30,44 +30,54 @@ logger = logging.getLogger(__name__ + '.DatabaseJobSource') - def getBoardList_impl(self): - return [d.hostname for d in Device.objects.all()] - def deferForDB(self, func, *args, **kw): def wrapper(*args, **kw): + # If there is no db connection yet on this thread, create a + # connection and immediately commit, because rolling back the + # first transaction on a connection loses the effect of + # settings.TIME_ZONE when using postgres (see + # https://code.djangoproject.com/ticket/17062). + transaction.enter_transaction_management() try: - return func(*args, **kw) - except (DatabaseError, OperationalError, InterfaceError), error: - message = str(error) - if message == 'connection already closed' or \ - message.startswith( - 'terminating connection due to administrator command') or \ - message.startswith( - 'could not connect to server: Connection refused'): - self.logger.warning( - 'Forcing reconnection on next db access attempt') - if connection.connection: - if not connection.connection.closed: - connection.connection.close() - connection.connection = None - raise + if connection.connection is None: + connection.cursor().close() + assert connection.connection is not None + transaction.commit() + try: + return func(*args, **kw) + except (DatabaseError, OperationalError, InterfaceError), error: + message = str(error) + if message == 'connection already closed' or \ + message.startswith( + 'terminating connection due to administrator command') or \ + message.startswith( + 'could not connect to server: Connection refused'): + self.logger.warning( + 'Forcing reconnection on next db access attempt') + if connection.connection: + if not connection.connection.closed: + connection.connection.close() + connection.connection = None + raise + finally: + # In Django 1.2, the commit_manually() etc decorators only + # commit or rollback the transaction if Django thinks there's + # been a write to the database. We don't want to leave + # transactions dangling under any circumstances so we + # unconditionally issue a rollback. This might be a teensy + # bit wastful, but it wastes a lot less time than figuring out + # why your south migration appears to have got stuck... + transaction.rollback() + transaction.leave_transaction_management() return deferToThread(wrapper, *args, **kw) + def getBoardList_impl(self): + return [d.hostname for d in Device.objects.all()] + def getBoardList(self): return self.deferForDB(self.getBoardList_impl) - @transaction.commit_manually() def getJobForBoard_impl(self, board_name): - # If there is no db connection yet on this thread, create a connection - # and immediately commit, because rolling back the first transaction - # on a connection loses the effect of settings.TIME_ZONE when using - # postgres (see https://code.djangoproject.com/ticket/17062) and this - # method has to be able to roll back to avoid assigning the same job - # to multiple boards. - if connection.connection is None: - connection.cursor().close() - assert connection.connection is not None - transaction.commit() while True: device = Device.objects.get(hostname=board_name) if device.status != Device.IDLE: @@ -109,21 +119,13 @@ transaction.commit() return json_data else: - # We don't really need to rollback here, as no modifying - # operations have been made to the database. But Django is - # stupi^Wconservative and assumes the queries that have been - # issued might have been modifications. - # See https://code.djangoproject.com/ticket/16491. - transaction.rollback() return None def getJobForBoard(self, board_name): return self.deferForDB(self.getJobForBoard_impl, board_name) - @transaction.commit_on_success() def getLogFileForJobOnBoard_impl(self, board_name): device = Device.objects.get(hostname=board_name) - device.status = Device.IDLE job = device.current_job log_file = job.log_file log_file.file.close() @@ -133,7 +135,6 @@ def getLogFileForJobOnBoard(self, board_name): return self.deferForDB(self.getLogFileForJobOnBoard_impl, board_name) - @transaction.commit_on_success() def jobCompleted_impl(self, board_name): self.logger.debug('marking job as complete on %s', board_name) device = Device.objects.get(hostname=board_name) @@ -162,7 +163,6 @@ def jobCompleted(self, board_name): return self.deferForDB(self.jobCompleted_impl, board_name) - @transaction.commit_on_success() def jobOobData_impl(self, board_name, key, value): self.logger.info( "oob data received for %s: %s: %s", board_name, key, value) @@ -170,13 +170,13 @@ device = Device.objects.get(hostname=board_name) device.current_job.results_link = value device.current_job.save() + transaction.commit() def jobOobData(self, board_name, key, value): return self.deferForDB(self.jobOobData_impl, board_name, key, value) def jobCheckForCancellation_impl(self, board_name): device = Device.objects.get(hostname=board_name) - device.status = Device.IDLE job = device.current_job return job.status != TestJob.RUNNING