Changeset 153

Show
Ignore:
Timestamp:
04/26/08 15:34:30 (4 months ago)
Author:
njriley
Message:

Handle database failure on reads.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • sucrose/trunk/python/api/sucrose.py

    r152 r153  
    1818class Sucrose(object): 
    1919 
     20        @db.read_or_die 
    2021        def user_from_uin(self, uin): 
    2122                return self.users.filter_by(uin=uin).first() 
    2223 
     24        @db.read_or_die 
    2325        def tray_from_location(self, location): 
    2426                if not ( ( location[0] in '12345' and location[1] in '1237' ) or 
     
    2830                return self.trays.filter_by(machine_tray_id=int(location)).first() 
    2931 
    30  
     32        @db.read_or_die 
    3133        def item_from_tray(self, tray): 
    3234                return self.items.filter_by(item_id=tray.item_id).first() 
    3335 
     36        @db.read_or_die 
    3437        def balance_for_user(self, user): 
    3538                return self.vending.filter_by(uid=user.uid).first().balance 
    3639 
     40        @db.read_or_die 
    3741        def recent_purchases_for_user(self, user): 
    3842                transactions = self.transactions.filter_by(user=user).\ 
     
    4246                return [(t.item_name, t.cost) for t in transactions] 
    4347 
     48        @db.read_or_die 
    4449        def total_spent_by_user(self, user): 
    4550                return self.transactions.filter_by(user=user).\ 
  • sucrose/trunk/python/mysql/db.py

    r121 r153  
    1 from sqlalchemy import create_engine, Table, Column, Integer, Numeric, String,
    2      MetaData, ForeignKey 
     1from sqlalchemy import create_engine, Table, Column, Integer, Numeric,
     2                       String, MetaData, ForeignKey 
    33from sqlalchemy.databases.mysql import MSEnum, MSInteger 
    44from sqlalchemy.ext.declarative import declarative_base 
     
    66from sqlalchemy.orm import mapper, relation, sessionmaker 
    77 
    8 DB_URL = 'mysql://soda:m568EXUFS@db1.acm.uiuc.edu' 
     8DB_URL = 'mysql://soda:m568EXUFS@db2.acm.uiuc.edu' 
    99# XXX change 'sucrose' to 'vending' once caffeine is migrated 
    1010_sucrose = create_engine('%s/sucrose' % DB_URL) 
     
    7272_Session = sessionmaker(transactional=False, autoflush=False, twophase=True) 
    7373session = _Session() 
     74 
     75try: 
     76    from functools import wraps 
     77except ImportError: 
     78    wraps = lambda wrapped: lambda wrapper: wrapper 
     79from sqlalchemy.exceptions import DBAPIError 
     80import logging, os 
     81def read_or_die(f): 
     82    """ 
     83    Retry a repeatable database read operation a few times before 
     84    exiting - handling temporary database failure. 
     85    """ 
     86    @wraps(f) 
     87    def wrapper(self, *args, **kw): 
     88        for retry_count in xrange(5, 0, -1): 
     89            try: 
     90                return f(self, *args, **kw) 
     91            except DBAPIError, e: 
     92                logging.warn('Retrying database query - %d left' % retry_count, 
     93                             exc_info=True) 
     94            except ValueError, e: # XXX replace with Sucrose-specific exception 
     95                raise 
     96            except: 
     97                logging.critical('Unexpected exception on DB read', 
     98                                 exc_info=True) 
     99                os._exit(1) 
     100        logging.critical('No retries left') 
     101        os._exit(1) 
     102    return wrapper 
     103