def update(self, queue, claim_id, metadata, project=None): if project is None: project = '' cid = utils.cid_decode(claim_id) if cid is None: raise errors.ClaimDoesNotExist(claim_id, queue, project) age = utils.get_age(tables.Claims.c.created) with self.driver.trans() as trans: qid = utils.get_qid(self.driver, queue, project) update = tables.Claims.update().where(sa.and_( tables.Claims.c.ttl > age, tables.Claims.c.id == cid, tables.Claims.c.id == qid)) update = update.values(ttl=metadata['ttl']) res = trans.execute(update) if res.rowcount != 1: raise errors.ClaimDoesNotExist(claim_id, queue, project) update = (tables.Messages.update(). values(ttl=metadata['ttl'] + metadata['grace']). where(sa.and_( tables.Messages.c.ttl < metadata['ttl'], tables.Messages.c.cid == cid))) trans.execute(update)
def post(self, queue, messages, client_uuid, project): if project is None: project = '' with self.driver.trans() as trans: qid = utils.get_qid(self.driver, queue, project) # Delete the expired messages and_stmt = sa.and_( tables.Messages.c.ttl <= sfunc.now() - tables.Messages.c.created, tables.Messages.c.qid == qid) statement = tables.Messages.delete().where(and_stmt) trans.execute(statement) # executemany() sets lastrowid to None, so no matter we manually # generate the IDs or not, we still need to query for it. def it(): for m in messages: yield dict(qid=qid, ttl=m['ttl'], body=utils.json_encode(m['body']), client=str(client_uuid)) result = trans.execute(tables.Messages.insert(), list(it())) statement = sa.sql.select([tables.Messages.c.id]) statement = statement.limit(result.rowcount) statement = statement.order_by(tables.Messages.c.id.desc()) result = trans.execute(statement).fetchall() return [utils.msgid_encode(i[0]) for i in reversed(result)]
def delete(self, queue, claim_id, project=None): if project is None: project = '' cid = utils.cid_decode(claim_id) if cid is None: return with self.driver.trans() as trans: try: # NOTE(flaper87): This could probably use some # joins and be just 1 query. qid = utils.get_qid(self.driver, queue, project) except errors.QueueDoesNotExist: return and_stmt = sa.and_(tables.Claims.c.id == cid, tables.Claims.c.qid == qid) dlt = tables.Claims.delete().where(and_stmt) trans.execute(dlt) update = (tables.Messages.update().values(cid=None). where(tables.Messages.c.cid == cid)) trans.execute(update)
def post(self, queue, messages, client_uuid, project): if project is None: project = '' with self.driver.trans() as trans: qid = utils.get_qid(self.driver, queue, project) # Delete the expired messages and_stmt = sa.and_(tables.Messages.c.ttl <= sfunc.now() - tables.Messages.c.created, tables.Messages.c.qid == qid) statement = tables.Messages.delete().where(and_stmt) trans.execute(statement) # executemany() sets lastrowid to None, so no matter we manually # generate the IDs or not, we still need to query for it. def it(): for m in messages: yield dict(qid=qid, ttl=m['ttl'], body=utils.json_encode(m['body']), client=str(client_uuid)) result = trans.execute(tables.Messages.insert(), list(it())) statement = sa.sql.select([tables.Messages.c.id]) statement = statement.limit(result.rowcount) statement = statement.order_by(tables.Messages.c.id.desc()) result = trans.execute(statement).fetchall() return [utils.msgid_encode(i[0]) for i in reversed(result)]
def pop(self, queue_name, limit, project=None): if project is None: project = '' with self.driver.trans() as trans: sel = sa.sql.select([ tables.Messages.c.id, tables.Messages.c.body, tables.Messages.c.ttl, tables.Messages.c.created, tables.Messages.c.cid ]) j = sa.join(tables.Messages, tables.Queues, tables.Messages.c.qid == tables.Queues.c.id) sel = sel.select_from(j) and_clause = self._and_stmt_with_ttl(queue_name, project) and_clause.append(tables.Messages.c.cid == (None)) sel = sel.where(sa.and_(*and_clause)) sel = sel.limit(limit) records = trans.execute(sel) now = timeutils.utcnow_ts() messages = [] message_ids = [] for id, body, ttl, created, cid in records: messages.append({ 'id': utils.msgid_encode(id), 'ttl': ttl, 'age': now - calendar.timegm(created.timetuple()), 'body': utils.json_decode(body), 'claim_id': utils.cid_encode(cid) if cid else None, }) message_ids.append(id) statement = tables.Messages.delete() qid = utils.get_qid(self.driver, queue_name, project) and_stmt = [ tables.Messages.c.id.in_(message_ids), tables.Messages.c.qid == qid ] trans.execute(statement.where(sa.and_(*and_stmt))) return messages
def create(self, queue, metadata, project=None, limit=storage.DEFAULT_MESSAGES_PER_CLAIM): if project is None: project = '' with self.driver.trans() as trans: try: qid = utils.get_qid(self.driver, queue, project) except errors.QueueDoesNotExist: return None, iter([]) # Clean up all expired claims in this queue dlt = tables.Claims.delete().where( sa.and_( tables.Claims.c.ttl <= utils.get_age( tables.Claims.c.created), tables.Claims.c.qid == qid)) trans.execute(dlt) ins = tables.Claims.insert().values(qid=qid, ttl=metadata['ttl']) res = trans.execute(ins) cid = res.lastrowid and_stmt = sa.and_( tables.Messages.c.cid == (None), tables.Messages.c.ttl > sfunc.now() - tables.Messages.c.created, tables.Messages.c.qid == qid) sel = sa.sql.select([tables.Messages.c.id], and_stmt).limit(limit) records = [t[0] for t in trans.execute(sel)] and_stmt = sa.and_(tables.Messages.c.id.in_(records)) update = tables.Messages.update().values(cid=cid).where(and_stmt) trans.execute(update) # NOTE(flaper87): I bet there's a better way # to do this. messages_ttl = metadata['ttl'] + metadata['grace'] update = (tables.Messages.update().values(ttl=messages_ttl).where( sa.and_(tables.Messages.c.ttl < messages_ttl, tables.Messages.c.cid == cid))) trans.execute(update) return (utils.cid_encode(int(cid)), list(self.__get(cid, trans)))
def pop(self, queue_name, limit, project=None): if project is None: project = '' with self.driver.trans() as trans: sel = sa.sql.select([tables.Messages.c.id, tables.Messages.c.body, tables.Messages.c.ttl, tables.Messages.c.created, tables.Messages.c.cid]) j = sa.join(tables.Messages, tables.Queues, tables.Messages.c.qid == tables.Queues.c.id) sel = sel.select_from(j) and_clause = self._and_stmt_with_ttl(queue_name, project) and_clause.append(tables.Messages.c.cid == (None)) sel = sel.where(sa.and_(*and_clause)) sel = sel.limit(limit) records = trans.execute(sel) now = timeutils.utcnow_ts() messages = [] message_ids = [] for id, body, ttl, created, cid in records: messages.append({ 'id': utils.msgid_encode(id), 'ttl': ttl, 'age': now - calendar.timegm(created.timetuple()), 'body': utils.json_decode(body), 'claim_id': utils.cid_encode(cid) if cid else None, }) message_ids.append(id) statement = tables.Messages.delete() qid = utils.get_qid(self.driver, queue_name, project) and_stmt = [tables.Messages.c.id.in_(message_ids), tables.Messages.c.qid == qid] trans.execute(statement.where(sa.and_(*and_stmt))) return messages
def create(self, queue, metadata, project=None, limit=storage.DEFAULT_MESSAGES_PER_CLAIM): if project is None: project = '' with self.driver.trans() as trans: try: qid = utils.get_qid(self.driver, queue, project) except errors.QueueDoesNotExist: return None, iter([]) # Clean up all expired claims in this queue dlt = tables.Claims.delete().where(sa.and_( tables.Claims.c.ttl <= utils.get_age(tables.Claims.c.created), tables.Claims.c.qid == qid)) trans.execute(dlt) ins = tables.Claims.insert().values(qid=qid, ttl=metadata['ttl']) res = trans.execute(ins) cid = res.lastrowid and_stmt = sa.and_(tables.Messages.c.cid == (None), tables.Messages.c.ttl > sfunc.now() - tables.Messages.c.created, tables.Messages.c.qid == qid) sel = sa.sql.select([tables.Messages.c.id], and_stmt).limit(limit) records = [t[0] for t in trans.execute(sel)] and_stmt = sa.and_(tables.Messages.c.id.in_(records)) update = tables.Messages.update().values(cid=cid).where(and_stmt) trans.execute(update) # NOTE(flaper87): I bet there's a better way # to do this. messages_ttl = metadata['ttl'] + metadata['grace'] update = (tables.Messages.update().values(ttl=messages_ttl). where(sa.and_( tables.Messages.c.ttl < messages_ttl, tables.Messages.c.cid == cid))) trans.execute(update) return (utils.cid_encode(int(cid)), list(self.__get(cid, trans)))
def bulk_delete(self, queue, message_ids, project): if project is None: project = '' message_ids = [id for id in map(utils.msgid_decode, message_ids) if id] with self.driver.trans() as trans: try: qid = utils.get_qid(self.driver, queue, project) except errors.QueueDoesNotExist: return statement = tables.Messages.delete() and_stmt = [tables.Messages.c.id.in_(message_ids), tables.Messages.c.qid == qid] trans.execute(statement.where(sa.and_(*and_stmt)))
def bulk_delete(self, queue, message_ids, project): if project is None: project = '' message_ids = [id for id in map(utils.msgid_decode, message_ids) if id] with self.driver.trans() as trans: try: qid = utils.get_qid(self.driver, queue, project) except errors.QueueDoesNotExist: return statement = tables.Messages.delete() and_stmt = [ tables.Messages.c.id.in_(message_ids), tables.Messages.c.qid == qid ] trans.execute(statement.where(sa.and_(*and_stmt)))
def _stats(self, name, project): if project is None: project = '' qid = utils.get_qid(self.driver, name, project) sel = sa.sql.select([ sa.sql.select([sa.func.count(tables.Messages.c.id)], sa.and_( tables.Messages.c.qid == qid, tables.Messages.c.cid != (None), tables.Messages.c.ttl > sfunc.now() - tables.Messages.c.created)), sa.sql.select([sa.func.count(tables.Messages.c.id)], sa.and_( tables.Messages.c.qid == qid, tables.Messages.c.cid == (None), tables.Messages.c.ttl > sfunc.now() - tables.Messages.c.created)) ]) claimed, free = self.driver.get(sel) total = free + claimed message_stats = { 'claimed': claimed, 'free': free, 'total': total, } try: message_controller = self.driver.message_controller oldest = message_controller.first(name, project, sort=1) newest = message_controller.first(name, project, sort=-1) except errors.QueueIsEmpty: pass else: message_stats['oldest'] = utils.stat_message(oldest) message_stats['newest'] = utils.stat_message(newest) return {'messages': message_stats}
def first(self, queue, project=None, sort=1): if project is None: project = '' qid = utils.get_qid(self.driver, queue, project) sel = sa.sql.select([ tables.Messages.c.id, tables.Messages.c.body, tables.Messages.c.ttl, tables.Messages.c.created ], sa.and_( tables.Messages.c.ttl > sfunc.now() - tables.Messages.c.created, tables.Messages.c.qid == qid)) if sort not in (1, -1): raise ValueError(u'sort must be either 1 (ascending) ' u'or -1 (descending)') order = sa.asc if sort == -1: order = sa.desc sel = sel.order_by(order(tables.Messages.c.id)) try: id, body, ttl, created = self.driver.get(sel) except utils.NoResult: raise errors.QueueIsEmpty(queue, project) created_iso = timeutils.isotime(created) return { 'id': utils.msgid_encode(int(id)), 'ttl': ttl, 'created': created_iso, 'age': int((timeutils.utcnow() - created).seconds), 'body': body, }
def first(self, queue, project=None, sort=1): if project is None: project = '' qid = utils.get_qid(self.driver, queue, project) sel = sa.sql.select([tables.Messages.c.id, tables.Messages.c.body, tables.Messages.c.ttl, tables.Messages.c.created], sa.and_( tables.Messages.c.ttl > sfunc.now() - tables.Messages.c.created, tables.Messages.c.qid == qid)) if sort not in (1, -1): raise ValueError(u'sort must be either 1 (ascending) ' u'or -1 (descending)') order = sa.asc if sort == -1: order = sa.desc sel = sel.order_by(order(tables.Messages.c.id)) try: id, body, ttl, created = self.driver.get(sel) except utils.NoResult: raise errors.QueueIsEmpty(queue, project) created_iso = timeutils.isotime(created) return { 'id': utils.msgid_encode(int(id)), 'ttl': ttl, 'created': created_iso, 'age': int((timeutils.utcnow() - created).seconds), 'body': body, }