Example #1
0
def process_votes_single(qname, limit=0):
    # limit is taken but ignored for backwards compatibility

    @g.stats.amqp_processor(qname)
    def _handle_vote(msg):
        #assert(len(msgs) == 1)
        r = pickle.loads(msg.body)

        uid, tid, dir, ip, organic, cheater = r
        voter = Account._byID(uid, data=True)
        votee = Thing._by_fullname(tid, data=True)
        if isinstance(votee, Comment):
            update_comment_votes([votee])

        # I don't know how, but somebody is sneaking in votes
        # for subreddits
        if isinstance(votee, (Link, Comment)):
            print(voter, votee, dir, ip, organic, cheater)
            handle_vote(voter,
                        votee,
                        dir,
                        ip,
                        organic,
                        cheater=cheater,
                        foreground=True)

    amqp.consume_items(qname, _handle_vote, verbose=False)
Example #2
0
def process_liveupdate_scraper_q():
    @g.stats.amqp_processor('liveupdate_scraper_q')
    def _handle_q(msg):
        d = json.loads(msg.body)

        event_id = d.get(
            "event_id") or d["event_fullname"][len("LiveUpdateEvent_"):]
        liveupdate_id = d["liveupdate_id"]

        try:
            fn = TimeoutFunction(parse_embeds, 10)
            liveupdate = fn(event_id, liveupdate_id)
        except TimeoutFunctionException:
            g.log.warning("Timed out on %s::%s", event_id, liveupdate_id)
            return
        except Exception as e:
            g.log.warning("Failed to scrape %s::%s: %r", event_id,
                          liveupdate_id, e)
            return

        if not liveupdate:
            return

        if not liveupdate.embeds and not liveupdate.mobile_embeds:
            return

        payload = {
            "liveupdate_id": "LiveUpdate_" + liveupdate_id,
            "media_embeds": liveupdate.embeds,
            "mobile_embeds": liveupdate.mobile_embeds,
        }
        send_event_broadcast(event_id, type="embeds_ready", payload=payload)

    amqp.consume_items('liveupdate_scraper_q', _handle_q, verbose=False)
def process_adzerk():
    @g.stats.amqp_processor('adzerk_q')
    def _handle_adzerk(msg):
        data = json.loads(msg.body)
        g.log.debug('data: %s' % data)

        action = data.get('action')

        if action == 'deactivate_orphaned_flight':
            _deactivate_orphaned_flight(data['flight'])
            return

        link = Link._by_fullname(data['link'], data=True)
        if data['campaign']:
            campaign = PromoCampaign._by_fullname(data['campaign'], data=True)
        else:
            campaign = None

        if action == 'update_adzerk':
            if 'triggered_by' in data and data['triggered_by'] is not None:
                triggered_by = Account._by_fullname(data['triggered_by'], data=True)
            else:
                triggered_by = None

            _update_adzerk(link, campaign, triggered_by)

        elif action == 'deactivate_overdelivered':
            _deactivate_overdelivered(link, campaign)

    amqp.consume_items('adzerk_q', _handle_adzerk, verbose=False)
Example #4
0
def process_liveupdate_scraper_q():
    @g.stats.amqp_processor('liveupdate_scraper_q')
    def _handle_q(msg):
        d = json.loads(msg.body)

        try:
            fn = TimeoutFunction(parse_embeds, 10)
            liveupdate = fn(d['event_id'], d['liveupdate_id'])
        except TimeoutFunctionException:
            g.log.warning("Timed out on %s::%s", d["event_id"],
                          d["liveupdate_id"])
            return
        except Exception as e:
            g.log.warning("Failed to scrape %s::%s: %r", d["event_id"],
                          d["liveupdate_id"], e)
            return

        payload = {
            "liveupdate_id": "LiveUpdate_" + d['liveupdate_id'],
            "media_embeds": liveupdate.embeds,
            "mobile_embeds": liveupdate.mobile_embeds,
        }
        send_event_broadcast(d['event_id'],
                             type="embeds_ready",
                             payload=payload)

    amqp.consume_items('liveupdate_scraper_q', _handle_q, verbose=False)
Example #5
0
def process_votes(qname, limit=0):
    # limit is taken but ignored for backwards compatibility

    @g.stats.amqp_processor(qname)
    def _handle_vote(msg):
        timer = stats.get_timer("service_time." + qname)
        timer.start()

        #assert(len(msgs) == 1)
        r = pickle.loads(msg.body)

        uid, tid, dir, ip, organic, cheater = r
        voter = Account._byID(uid, data=True)
        votee = Thing._by_fullname(tid, data = True)
        timer.intermediate("preamble")

        if isinstance(votee, Comment):
            update_comment_votes([votee])
            timer.intermediate("update_comment_votes")

        # I don't know how, but somebody is sneaking in votes
        # for subreddits
        if isinstance(votee, (Link, Comment)):
            print (voter, votee, dir, ip, organic, cheater)
            handle_vote(voter, votee, dir, ip, organic,
                        cheater = cheater, foreground=True, timer=timer)

    amqp.consume_items(qname, _handle_vote, verbose = False)
def process_liveupdate_scraper_q():
    @g.stats.amqp_processor('liveupdate_scraper_q')
    def _handle_q(msg):
        d = json.loads(msg.body)

        try:
            fn = TimeoutFunction(parse_embeds, 10)
            liveupdate = fn(d['event_id'], d['liveupdate_id'])
        except TimeoutFunctionException:
            g.log.warning(
                "Timed out on %s::%s", d["event_id"], d["liveupdate_id"])
            return
        except Exception as e:
            g.log.warning("Failed to scrape %s::%s: %r",
                d["event_id"], d["liveupdate_id"], e)
            return

        payload = {
            "liveupdate_id": "LiveUpdate_" + d['liveupdate_id'],
            "media_embeds": liveupdate.embeds,
            "mobile_embeds": liveupdate.mobile_embeds,
        }
        send_event_broadcast(d['event_id'],
                             type="embeds_ready",
                             payload=payload)

    amqp.consume_items('liveupdate_scraper_q', _handle_q, verbose=False)
Example #7
0
def run():
    @g.stats.amqp_processor("butler_q")
    def process_message(msg):
        fname = msg.body
        item = Thing._by_fullname(fname, data=True)
        monitor_mentions(item)

    amqp.consume_items("butler_q", process_message, verbose=True)
Example #8
0
def run():
    @g.stats.amqp_processor("butler_q")
    def process_message(msg):
        fname = msg.body
        item = Thing._by_fullname(fname, data=True)
        monitor_mentions(item)

    amqp.consume_items("butler_q", process_message, verbose=True)
Example #9
0
def consume_link_vote_queue(qname="vote_link_q"):
    @g.stats.amqp_processor(qname)
    def process_message(msg):
        vote_data = json.loads(msg.body)
        hook = hooks.get_hook('vote.validate_vote_data')
        if hook.call_until_return(msg=msg, vote_data=vote_data) is False:
            # Corrupt records in the queue. Ignore them.
            print "Ignoring invalid vote by %s on %s %s" % (
                    vote_data.get('user_id', '<unknown>'),
                    vote_data.get('thing_fullname', '<unknown>'),
                    vote_data)
            return

        timer = g.stats.get_timer("link_vote_processor")
        timer.start()

        user = Account._byID(vote_data.pop("user_id"))
        link = Link._by_fullname(vote_data.pop("thing_fullname"))

        # create the vote and update the voter's liked/disliked under lock so
        # that the vote state and cached query are consistent
        lock_key = "vote-%s-%s" % (user._id36, link._fullname)
        with g.make_lock("voting", lock_key, timeout=5):
            print "Processing vote by %s on %s %s" % (user, link, vote_data)

            try:
                vote = Vote(
                    user,
                    link,
                    direction=vote_data["direction"],
                    date=datetime.utcfromtimestamp(vote_data["date"]),
                    data=vote_data["data"],
                    event_data=vote_data.get("event_data"),
                    # CUSTOM: voting model
                    vote_direction=vote_data["vote_direction"],
                )
            except TypeError as e:
                # a vote on an invalid type got in the queue, just skip it
                g.log.exception("Invalid type: %r", e.message)
                return

            vote.commit()
            timer.intermediate("create_vote_object")

            update_user_liked(vote)
            timer.intermediate("voter_likes")

        vote_valid = vote.is_automatic_initial_vote or vote.effects.affects_score
        link_valid = not (link._spam or link._deleted)
        if vote_valid and link_valid:
            add_to_author_query_q(link)
            add_to_subreddit_query_q(link)
            add_to_domain_query_q(link)

        timer.stop()
        timer.flush()

    amqp.consume_items(qname, process_message, verbose=False)
def run_summary_email_q(verbose=False):
    queue_name = 'summary_email_q'

    @g.stats.amqp_processor(queue_name)
    def _run_summary_email(msg):
        account_thing_id = int(msg.body)
        send_account_summary_email(account_thing_id)

    amqp.consume_items(queue_name, _run_summary_email, verbose=verbose)
Example #11
0
def consume_vote_queue(queue):
    @g.stats.amqp_processor(queue)
    def process_message(msg):
		# msg is *PROBABLY* json
        timer = g.stats.get_timer("new_voting.%s" % queue)
        timer.start()

		# json being loaded into a python object
		# it has the fields "user_id", "thing_fullname"
		# a thing is a database object
		# it's a link, comment, post, whatever, everything can be upvoted/downvoted
        vote_data = json.loads(msg.body)
        hook = hooks.get_hook('vote.validate_vote_data')
        if hook.call_until_return(msg=msg, vote_data=vote_data) is False:
            # Corrupt records in the queue. Ignore them.
            print "Ignoring invalid vote by %s on %s %s" % (
                    vote_data.get('user_id', '<unknown>'),
                    vote_data.get('thing_fullname', '<unknown>'),
                    vote_data)
            return

		# this gets the user from database/cache (either memcached or postgres, whatever)
        user = Account._byID(vote_data.pop("user_id"), data=True)
        thing = Thing._by_fullname(vote_data.pop("thing_fullname"), data=True)

        timer.intermediate("preamble")

		# this gets a servers-wide lock
		# I mean, a bunch of consumers might be consuming items that use the same "thing" (same database object)
		# so, you want a global lock to avoid them from f*****g eachother up 
		# memcachd stores the lock, atomically
        lock_key = "vote-%s-%s" % (user._id36, thing._fullname)
        with g.make_lock("voting", lock_key, timeout=5):
            print "Processing vote by %s on %s %s" % (user, thing, vote_data)

            try:
                vote = Vote(
                    user,
                    thing,
                    direction=vote_data["direction"],
                    date=datetime.utcfromtimestamp(vote_data["date"]),
                    data=vote_data["data"],
                    event_data=vote_data.get("event_data"),
                )
            except TypeError as e:
                # a vote on an invalid type got in the queue, just skip it
                g.log.exception("Invalid type: %r", e.message)
                return

            timer.intermediate("create_vote_obj")

            vote.commit()

            timer.flush()

    amqp.consume_items(queue, process_message, verbose=False)
Example #12
0
def consume_link_vote_queue(qname="vote_link_q"):
    @g.stats.amqp_processor(qname)
    def process_message(msg):
        vote_data = json.loads(msg.body)
        hook = hooks.get_hook('vote.validate_vote_data')
        if hook.call_until_return(msg=msg, vote_data=vote_data) is False:
            # Corrupt records in the queue. Ignore them.
            print "Ignoring invalid vote by %s on %s %s" % (
                    vote_data.get('user_id', '<unknown>'),
                    vote_data.get('thing_fullname', '<unknown>'),
                    vote_data)
            return

        timer = g.stats.get_timer("link_vote_processor")
        timer.start()

        user = Account._byID(vote_data.pop("user_id"))
        link = Link._by_fullname(vote_data.pop("thing_fullname"))

        # create the vote and update the voter's liked/disliked under lock so
        # that the vote state and cached query are consistent
        lock_key = "vote-%s-%s" % (user._id36, link._fullname)
        with g.make_lock("voting", lock_key, timeout=5):
            print "Processing vote by %s on %s %s" % (user, link, vote_data)

            try:
                vote = Vote(
                    user,
                    link,
                    direction=vote_data["direction"],
                    date=datetime.utcfromtimestamp(vote_data["date"]),
                    data=vote_data["data"],
                    event_data=vote_data.get("event_data"),
                )
            except TypeError as e:
                # a vote on an invalid type got in the queue, just skip it
                g.log.exception("Invalid type: %r", e.message)
                return

            vote.commit()
            timer.intermediate("create_vote_object")

            update_user_liked(vote)
            timer.intermediate("voter_likes")

        vote_valid = vote.is_automatic_initial_vote or vote.effects.affects_score
        link_valid = not (link._spam or link._deleted)
        if vote_valid and link_valid:
            add_to_author_query_q(link)
            add_to_subreddit_query_q(link)
            add_to_domain_query_q(link)

        timer.stop()
        timer.flush()

    amqp.consume_items(qname, process_message, verbose=False)
Example #13
0
def run_subreddit_maker():
    @g.stats.amqp_processor('robin_subreddit_maker_q')
    def process_subreddit_maker(msg):
        room_id = msg.body
        try:
            room = RobinRoom._byID(room_id)
        except tdb_cassandra.NotFound:
            try:
                room = RobinRoomDead._byID(room_id)
            except tdb_cassandra.NotFound:
                print "can't find room %s, giving up" % room_id
        print 'creating sr for room %s' % room

        subreddit = room.create_sr()
        print 'got %s from room.create_sr()' % subreddit

        if subreddit:
            g.stats.simple_event("robin.subreddit.created")
            participant_ids = room.get_all_participants()
            participants = [
                Account._byID(participant_id)
                for participant_id in participant_ids
            ]
            moderators = participants[:5]

            print 'adding moderators to %s' % subreddit
            for moderator in moderators:
                subreddit.add_moderator(moderator)

            print 'adding contributors to %s' % subreddit
            g.stats.simple_event(
                "robin.subreddit.contributors_added",
                delta=len(participants),
            )
            for participant in participants:
                # To be replaced with UserRel hacking?
                subreddit.add_contributor(participant)
                send_sr_message(subreddit, participant)

            payload = {
                "body": subreddit.name,
            }

            websockets.send_broadcast(
                namespace="/robin/" + room.id,
                type="continue",
                payload=payload,
            )
        else:
            g.stats.simple_event("robin.subreddit.creation_failed")
            print 'subreddit creation failed for room %s' % room.id

    amqp.consume_items('robin_subreddit_maker_q', process_subreddit_maker)
Example #14
0
def run_new_comments():
    """Add new incoming comments to the /comments page"""
    # this is done as a queue because otherwise the contention for the
    # lock on the query would be very high

    def _run_new_comment(msg):
        fname = msg.body
        comment = Comment._by_fullname(fname)

        add_queries([get_all_comments()], insert_items=[comment])

    amqp.consume_items("newcomments_q", _run_new_comment)
Example #15
0
def run_new_comments():
    """Add new incoming comments to the /comments page"""

    # this is done as a queue because otherwise the contention for the
    # lock on the query would be very high

    def _run_new_comment(msg):
        fname = msg.body
        comment = Comment._by_fullname(fname)

        add_queries([get_all_comments()], insert_items=[comment])

    amqp.consume_items('newcomments_q', _run_new_comment)
Example #16
0
def run():
    # Add watch to only update kwl when the keyword list changes
    @g.zookeeper.DataWatch("/keyword-targets")
    def watch_keywords(data, stats):
        global kwl
        kwl = json.loads(data)

    @g.stats.amqp_processor("keyword_target_q")
    def process_message(msg):
        fname = msg.body
        link = Link._by_fullname(fname, data=True)
        extract_keywords(link)

    amqp.consume_items("keyword_target_q", process_message, verbose=True)
Example #17
0
def consume_vote_queue(queue):
    @g.stats.amqp_processor(queue)
    def process_message(msg):
        timer = g.stats.get_timer("new_voting.%s" % queue)
        timer.start()

        vote_data = json.loads(msg.body)
        hook = hooks.get_hook('vote.validate_vote_data')
        if hook.call_until_return(msg=msg, vote_data=vote_data) is False:
            # Corrupt records in the queue. Ignore them.
            print "Ignoring invalid vote by %s on %s %s" % (
                    vote_data.get('user_id', '<unknown>'),
                    vote_data.get('thing_fullname', '<unknown>'),
                    vote_data)
            return

        # if it's an old-style vote, convert to the new format
        if "uid" in vote_data:
            vote_data = convert_old_vote_data(vote_data, msg.timestamp)

        user = Account._byID(vote_data.pop("user_id"), data=True)
        thing = Thing._by_fullname(vote_data.pop("thing_fullname"), data=True)

        timer.intermediate("preamble")

        lock_key = "vote-%s-%s" % (user._id36, thing._fullname)
        with g.make_lock("voting", lock_key, timeout=5):
            print "Processing vote by %s on %s %s" % (user, thing, vote_data)

            try:
                vote = Vote(
                    user,
                    thing,
                    direction=vote_data["direction"],
                    date=datetime.utcfromtimestamp(vote_data["date"]),
                    data=vote_data["data"],
                    event_data=vote_data.get("event_data"),
                )
            except TypeError as e:
                # a vote on an invalid type got in the queue, just skip it
                g.log.exception("Invalid type: %r", e.message)
                return

            timer.intermediate("create_vote_obj")

            vote.commit()

            timer.flush()

    amqp.consume_items(queue, process_message, verbose=False)
Example #18
0
def process_votes(limit=None):
    # limit is taken but ignored for backwards compatibility

    def _handle_vote(msg):
        r = pickle.loads(msg.body)

        uid, tid, dir, ip, organic, cheater = r
        voter = Account._byID(uid, data=True)
        votee = Thing._by_fullname(tid, data=True)

        print(voter, votee, dir, ip, organic, cheater)
        handle_vote(voter, votee, dir, ip, organic, cheater=cheater)

    amqp.consume_items('register_vote_q', _handle_vote)
Example #19
0
def run():
    @g.stats.amqp_processor("automoderator_q")
    def process_message(msg):
        if not ACCOUNT:
            return

        fullname = msg.body
        item = Thing._by_fullname(fullname, data=True)
        if not isinstance(item, (Link, Comment)):
            return

        subreddit = item.subreddit_slow

        wiki_page_id = wiki_id(subreddit._id36, "config/automoderator")
        wiki_page_fullname = "WikiPage_%s" % wiki_page_id
        last_edited = LastModified.get(wiki_page_fullname, "Edit")
        if not last_edited:
            return

        # initialize rules for the subreddit if we haven't already
        # or if the page has been edited since we last initialized
        need_to_init = False
        if subreddit._id not in rules_by_subreddit:
            need_to_init = True
        else:
            rules = rules_by_subreddit[subreddit._id]
            if last_edited > rules.init_time:
                need_to_init = True

        if need_to_init:
            wp = WikiPage.get(subreddit, "config/automoderator")
            rules = Ruleset(wp.content)
            rules_by_subreddit[subreddit._id] = rules

        if not rules:
            return

        try:
            TimeoutFunction(rules.apply_to_item, 2)(item)
            print "Checked %s from /r/%s" % (item, subreddit.name)
        except TimeoutFunctionException:
            print "Timed out on %s from /r/%s" % (item, subreddit.name)
        except KeyboardInterrupt:
            raise
        except:
            print "Error on %s from /r/%s" % (item, subreddit.name)
            print traceback.format_exc()

    amqp.consume_items("automoderator_q", process_message, verbose=False)
Example #20
0
def consume_vote_queue(queue):
    @g.stats.amqp_processor(queue)
    def process_message(msg):
        timer = g.stats.get_timer("new_voting.%s" % queue)
        timer.start()

        vote_data = json.loads(msg.body)
        hook = hooks.get_hook('vote.validate_vote_data')
        if hook.call_until_return(msg=msg, vote_data=vote_data) is False:
            # Corrupt records in the queue. Ignore them.
            print "Ignoring invalid vote by %s on %s %s" % (
                vote_data.get('user_id', '<unknown>'),
                vote_data.get('thing_fullname', '<unknown>'), vote_data)
            return

        # if it's an old-style vote, convert to the new format
        if "uid" in vote_data:
            vote_data = convert_old_vote_data(vote_data, msg.timestamp)

        user = Account._byID(vote_data.pop("user_id"), data=True)
        thing = Thing._by_fullname(vote_data.pop("thing_fullname"), data=True)

        timer.intermediate("preamble")

        lock_key = "vote-%s-%s" % (user._id36, thing._fullname)
        with g.make_lock("voting", lock_key, timeout=5):
            print "Processing vote by %s on %s %s" % (user, thing, vote_data)

            try:
                vote = Vote(
                    user,
                    thing,
                    direction=vote_data["direction"],
                    date=datetime.utcfromtimestamp(vote_data["date"]),
                    data=vote_data["data"],
                    event_data=vote_data.get("event_data"),
                )
            except TypeError as e:
                # a vote on an invalid type got in the queue, just skip it
                g.log.exception("Invalid type: %r", e.message)
                return

            timer.intermediate("create_vote_obj")

            vote.commit()

            timer.flush()

    amqp.consume_items(queue, process_message, verbose=False)
Example #21
0
def run_new_comments():
    """Add new incoming comments to the /comments page"""
    # this is done as a queue because otherwise the contention for the
    # lock on the query would be very high

    def _run_new_comment(msg):
        fname = msg.body
        comment = Comment._by_fullname(fname,data=True)
        sr = Subreddit._byID(comment.sr_id)

        add_queries([get_all_comments(),
                     get_sr_comments(sr)],
                    insert_items = [comment])

    amqp.consume_items('newcomments_q', _run_new_comment)
Example #22
0
def process_votes(limit=None):
    # limit is taken but ignored for backwards compatibility

    def _handle_vote(msg):
        r = pickle.loads(msg.body)

        uid, tid, dir, ip, organic, cheater = r
        voter = Account._byID(uid, data=True)
        votee = Thing._by_fullname(tid, data = True)

        print (voter, votee, dir, ip, organic, cheater)
        handle_vote(voter, votee, dir, ip, organic,
                    cheater = cheater)

    amqp.consume_items('register_vote_q', _handle_vote)
Example #23
0
def run():
    # Add watch to only update kwl when the keyword list changes
    @g.zookeeper.DataWatch("/keyword-targets")
    def watch_keywords(data, stats):
        global kwl
        kwl = json.loads(data)
        
    @g.stats.amqp_processor("keyword_target_q")
    def process_message(msg):
        fname = msg.body
        link = Link._by_fullname(fname, data=True)
        extract_keywords(link)

    amqp.consume_items("keyword_target_q",
                       process_message,
                       verbose=True)
def process_report_q():
    @g.stats.amqp_processor('adzerk_reporting_q')
    def _processor(message):
        data = json.loads(message.body)
        action = data.get("action")

        if action == "generate_daily_link_report":
            _handle_generate_daily_link_report(link_id=data.get("link_id"), )
        elif action == "generate_lifetime_campaign_report":
            _handle_generate_lifetime_campaign_report(
                campaign_id=data.get("campaign_id"), )
        else:
            g.log.warning("adzerk_reporting_q: unknown action - \"%s\"" %
                          action)

    amqp.consume_items("adzerk_reporting_q", _processor, verbose=False)
Example #25
0
def run():
    @g.stats.amqp_processor('scraper_q')
    def process_link(msg):
        fname = msg.body
        link = Link._by_fullname(msg.body, data=True)

        try:
            TimeoutFunction(_set_media, 30)(link)
        except TimeoutFunctionException:
            print "Timed out on %s" % fname
        except KeyboardInterrupt:
            raise
        except:
            print "Error fetching %s" % fname
            print traceback.format_exc()

    amqp.consume_items('scraper_q', process_link)
Example #26
0
def process():
    processor = Processor()
    processor.register("upsert_promotion", _handle_upsert_promotion)
    processor.register("upsert_campaign", _handle_upsert_campaign)
    processor.register("deactivate_campaign", _handle_deactivate_campaign)

    @g.stats.amqp_processor(DFP_QUEUE)
    def _handler(message):
        data = json.loads(message.body)
        g.log.debug("processing action: %s" % data)

        action = data.get("action")
        payload = data.get("payload")

        processor.call(action, payload)

    amqp.consume_items(DFP_QUEUE, _handler, verbose=False)
Example #27
0
def run():
    @g.stats.amqp_processor('scraper_q')
    def process_link(msg):
        fname = msg.body
        link = Link._by_fullname(fname, data=True)

        try:
            TimeoutFunction(_set_media, 30)(link, use_cache=True)
        except TimeoutFunctionException:
            print "Timed out on %s" % fname
        except KeyboardInterrupt:
            raise
        except:
            print "Error fetching %s" % fname
            print traceback.format_exc()

    amqp.consume_items('scraper_q', process_link)
Example #28
0
def run_steam_q():
    import requests

    session = requests.Session()
    session.headers.update({
        "User-Agent": g.useragent,
    })

    @g.stats.amqp_processor(QNAME)
    def _claim_hat(msg):
        data = json.loads(msg.body)

        account = Account._byID(int(data["user-id"]), data=True)
        if account.f2p != "claiming":
            g.log.warning("%r attempted to claim twice!", account)
            return

        user_team = scores.get_user_team(account)
        promo_id = g.steam_promo_items[user_team]

        g.stats.event_count("f2p.claim_hat", "item_%s" % promo_id)

        response = session.post(GRANT_URL,
                                data={
                                    "SteamID": data["steam-id"],
                                    "PromoID": promo_id,
                                    "key": g.steam_api_key,
                                    "format": "json",
                                })

        # band-aid for requests API change in v1.0.0
        if callable(response.json):
            response_data = response.json()
        else:
            response_data = response.json

        if response_data["result"]["status"] != 1:
            g.log.warning("Steam Promo for %r -> %r failed: %s", account,
                          data["steam-id"], response_data["statusDetail"])
            raise Exception

        account.f2p = "claimed"
        account._commit()

    amqp.consume_items(QNAME, _claim_hat, verbose=True)
Example #29
0
def run():
    def process_link(msg):
        def _process_link(fname):
            link = Link._by_fullname(fname, data=True)
            set_media(link)

        fname = msg.body
        try:
            TimeoutFunction(_process_link, 30)(fname)
        except TimeoutFunctionException:
            print "Timed out on %s" % fname
        except KeyboardInterrupt:
            raise
        except:
            print "Error fetching %s" % fname
            print traceback.format_exc()

    amqp.consume_items('scraper_q', process_link)
Example #30
0
def run():
    def process_link(msg):
        def _process_link(fname):
            link = Link._by_fullname(fname, data=True)
            set_media(link)

        fname = msg.body
        try:
            TimeoutFunction(_process_link, 30)(fname)
        except TimeoutFunctionException:
            print "Timed out on %s" % fname
        except KeyboardInterrupt:
            raise
        except:
            print "Error fetching %s" % fname
            print traceback.format_exc()

    amqp.consume_items('scraper_q', process_link)
Example #31
0
def process_modmail_email():
    @g.stats.amqp_processor("modmail_email_q")
    def process_message(msg):
        msg_dict = json.loads(msg.body)
        if msg_dict["event"] == "new_message":
            message_id36 = msg_dict["message_id36"]
            message = Message._byID36(message_id36, data=True)
            send_modmail_email(message)
        elif msg_dict["event"] == "blocked_muted":
            subreddit_id36 = msg_dict["subreddit_id36"]
            sr = Subreddit._byID36(subreddit_id36, data=True)
            parent_id36 = msg_dict["parent_id36"]
            parent = Message._byID36(parent_id36, data=True)
            sender_email = msg_dict["sender_email"]
            incoming_email_id = msg_dict["incoming_email_id"]
            send_blocked_muted_email(sr, parent, sender_email, incoming_email_id)

    amqp.consume_items("modmail_email_q", process_message)
Example #32
0
def process_modmail_email():
    @g.stats.amqp_processor("modmail_email_q")
    def process_message(msg):
        msg_dict = json.loads(msg.body)
        if msg_dict["event"] == "new_message":
            message_id36 = msg_dict["message_id36"]
            message = Message._byID36(message_id36, data=True)
            send_modmail_email(message)
        elif msg_dict["event"] == "blocked_muted":
            subreddit_id36 = msg_dict["subreddit_id36"]
            sr = Subreddit._byID36(subreddit_id36, data=True)
            parent_id36 = msg_dict["parent_id36"]
            parent = Message._byID36(parent_id36, data=True)
            sender_email = msg_dict["sender_email"]
            incoming_email_id = msg_dict["incoming_email_id"]
            send_blocked_muted_email(sr, parent, sender_email, incoming_email_id)

    amqp.consume_items("modmail_email_q", process_message)
def process_report_q():
    @g.stats.amqp_processor('adzerk_reporting_q')
    def _processor(message):
        data = json.loads(message.body)
        action = data.get("action")

        if action == "generate_daily_link_report":
            _handle_generate_daily_link_report(
                link_id=data.get("link_id"),
            )
        elif action == "generate_lifetime_campaign_report":
            _handle_generate_lifetime_campaign_report(
                campaign_id=data.get("campaign_id"),
            )
        else:
            g.log.warning("adzerk_reporting_q: unknown action - \"%s\"" % action)

    amqp.consume_items("adzerk_reporting_q", _processor, verbose=False)
def process_adzerk():
    @g.stats.amqp_processor('adzerk_q')
    def _handle_adzerk(msg):
        data = json.loads(msg.body)
        g.log.debug('data: %s' % data)
        action = data.get('action')
        if action == 'deactivate_link':
            link = Link._by_fullname(data['link'], data=True)
            _deactivate_link(link)
        elif action == 'deactivate_campaign':
            link = Link._by_fullname(data['link'], data=True)
            campaign = PromoCampaign._by_fullname(data['campaign'], data=True)
            _deactivate_campaign(link, campaign)
        elif action == 'update_adzerk':
            link = Link._by_fullname(data['link'], data=True)
            campaign = PromoCampaign._by_fullname(data['campaign'], data=True)
            _update_adzerk(link, campaign)
    amqp.consume_items('adzerk_q', _handle_adzerk, verbose=False)
Example #35
0
def run_commentstree():
    """Add new incoming comments to their respective comments trees"""
    def _run_commentstree(msg):
        fname = msg.body
        comment = Comment._by_fullname(fname, data=True)

        link = Link._byID(comment.link_id, data=True)

        try:
            add_comment_tree(comment, link)
        except KeyError:
            # Hackity hack. Try to recover from a corrupted comment
            # tree
            print "Trying to fix broken comments-tree."
            link_comments(link._id, _update=True)
            add_comment_tree(comment, link)

    amqp.consume_items('commentstree_q', _run_commentstree)
Example #36
0
def run_steam_q():
    import requests

    session = requests.Session()
    session.headers.update({
        "User-Agent": g.useragent,
    })

    @g.stats.amqp_processor(QNAME)
    def _claim_hat(msg):
        data = json.loads(msg.body)

        account = Account._byID(int(data["user-id"]), data=True)
        if account.f2p != "claiming":
            g.log.warning("%r attempted to claim twice!", account)
            return

        user_team = scores.get_user_team(account)
        promo_id = g.steam_promo_items[user_team]

        g.stats.event_count("f2p.claim_hat", "item_%s" % promo_id)

        response = session.post(GRANT_URL, data={
            "SteamID": data["steam-id"],
            "PromoID": promo_id,
            "key": g.steam_api_key,
            "format": "json",
        })

        # band-aid for requests API change in v1.0.0
        if callable(response.json):
            response_data = response.json()
        else:
            response_data = response.json

        if response_data["result"]["status"] != 1:
            g.log.warning("Steam Promo for %r -> %r failed: %r",
                          account, data["steam-id"],
                          response_data)
            raise Exception

        account.f2p = "claimed"
        account._commit()
    amqp.consume_items(QNAME, _claim_hat, verbose=True)
Example #37
0
def run():
    @g.stats.amqp_processor("robin_presence_q")
    def process_presence_update(msg):
        message_type = msg.delivery_info["routing_key"]
        payload = json.loads(msg.body)

        namespace = payload["namespace"]
        if not namespace.startswith("/robin/"):
            return

        user_id36 = posixpath.basename(namespace)
        room_namespace = posixpath.dirname(namespace)
        room_id = posixpath.basename(room_namespace)

        account = Account._byID36(user_id36, data=True, stale=True)
        try:
            room = RobinRoom._byID(room_id)
        except tdb_cassandra.NotFoundException:
            return

        if not room.is_participant(account):
            return

        presence_type = "join" if message_type == "websocket.connect" else "part"

        websockets.send_broadcast(
            namespace=room_namespace,
            type=presence_type,
            payload={
                "user": account.name,
            },
        )

        if presence_type == "join":
            ParticipantPresenceByRoom.mark_joined(room, account)
        else:
            ParticipantPresenceByRoom.mark_exited(room, account)


    amqp.consume_items(
        "robin_presence_q",
        process_presence_update,
        verbose=True,
    )
Example #38
0
def run():
    def callback(msg):
        # cr is a r2.lib.db.queries.CachedResults
        cr = pickle.loads(msg.body)
        iden = cr.query._iden()

        working_key = working_prefix + iden
        key = prefix + iden

        last_time = g.memcache.get(key)
        # check to see if we've computed this job since it was
        # added to the queue
        if last_time and last_time > msg.timestamp:
            print 'skipping, already computed ', key
            return

        if not cr.preflight_check():
            print 'skipping, preflight check failed', key
            return

        # check if someone else is working on this
        elif not g.memcache.add(working_key, 1, TIMEOUT):
            print 'skipping, someone else is working', working_key
            return

        print 'working: ', iden, cr.query._rules, cr.query._sort
        start = datetime.now()
        try:
            cr.update()
            g.memcache.set(key, datetime.now())

            cr.postflight()

        finally:
            g.memcache.delete(working_key)

        done = datetime.now()
        q_time_s = (done - msg.timestamp).seconds
        proc_time_s = (done - start).seconds + (
            (done - start).microseconds / 1000000.0)
        print('processed %s in %.6f seconds after %d seconds in queue' %
              (iden, proc_time_s, q_time_s))

    amqp.consume_items('prec_links', callback)
Example #39
0
def run():
    @g.stats.amqp_processor("robin_presence_q")
    def process_presence_update(msg):
        message_type = msg.delivery_info["routing_key"]
        payload = json.loads(msg.body)

        namespace = payload["namespace"]
        if not namespace.startswith("/robin/"):
            return

        user_id36 = posixpath.basename(namespace)
        room_namespace = posixpath.dirname(namespace)
        room_id = posixpath.basename(room_namespace)

        account = Account._byID36(user_id36, data=True, stale=True)
        try:
            room = RobinRoom._byID(room_id)
        except tdb_cassandra.NotFoundException:
            return

        if not room.is_participant(account):
            return

        presence_type = "join" if message_type == "websocket.connect" else "part"

        websockets.send_broadcast(
            namespace=room_namespace,
            type=presence_type,
            payload={
                "user": account.name,
            },
        )

        if presence_type == "join":
            ParticipantPresenceByRoom.mark_joined(room, account)
        else:
            ParticipantPresenceByRoom.mark_exited(room, account)

    amqp.consume_items(
        "robin_presence_q",
        process_presence_update,
        verbose=True,
    )
Example #40
0
def run():
    def callback(msg):
        # cr is a r2.lib.db.queries.CachedResults
        cr = pickle.loads(msg.body)
        iden = cr.query._iden()

        working_key = working_prefix + iden
        key = prefix + iden

        last_time = g.memcache.get(key)
        # check to see if we've computed this job since it was
        # added to the queue
        if last_time and last_time > msg.timestamp:
            print 'skipping, already computed ', key
            return

        if not cr.preflight_check():
            print 'skipping, preflight check failed', key
            return

        # check if someone else is working on this
        elif not g.memcache.add(working_key, 1, TIMEOUT):
            print 'skipping, someone else is working', working_key
            return

        print 'working: ', iden, cr.query._rules, cr.query._sort
        start = datetime.now()
        try:
            cr.update()
            g.memcache.set(key, datetime.now())

            cr.postflight()

        finally:
            g.memcache.delete(working_key)

        done = datetime.now()
        q_time_s = (done - msg.timestamp).seconds
        proc_time_s = (done - start).seconds + ((done - start).microseconds/1000000.0)
        print ('processed %s in %.6f seconds after %d seconds in queue'
               % (iden, proc_time_s, q_time_s))

    amqp.consume_items('prec_links', callback)
Example #41
0
def run_commentstree():
    """Add new incoming comments to their respective comments trees"""

    def _run_commentstree(msg):
        fname = msg.body
        comment = Comment._by_fullname(fname, data=True)

        link = Link._byID(comment.link_id,
                          data=True)

        try:
            add_comment_tree(comment, link)
        except KeyError:
            # Hackity hack. Try to recover from a corrupted comment
            # tree
            print "Trying to fix broken comments-tree."
            link_comments(link._id, _update=True)
            add_comment_tree(comment, link)

    amqp.consume_items('commentstree_q', _run_commentstree)
Example #42
0
def run_waitinglist():
    @g.stats.amqp_processor("robin_waitinglist_q")
    def process_waitinglist(msg):
        user_id36 = msg.body
        user = Account._byID36(user_id36, data=True, stale=True)
        if RobinRoom.get_room_for_user(user):
            print "%s already in room" % user.name
            return

        with g.make_lock("robin_room", "global"):
            current_room_id = g.gencache.get("current_robin_room")
            if not current_room_id:
                current_room = make_new_room()
            else:
                try:
                    current_room = RobinRoom._byID(current_room_id)
                except tdb_cassandra.NotFoundException:
                    current_room_id = None
                    current_room = make_new_room()

                if not current_room.is_alive or current_room.is_continued:
                    current_room_id = None
                    current_room = make_new_room()

            current_room.add_participants([user])
            print "added %s to %s" % (user.name, current_room.id)

            if current_room_id:
                g.gencache.delete("current_robin_room")
                current_room.persist_computed_name()
                websockets.send_broadcast(
                    namespace="/robin/" + current_room.id,
                    type="updated_name",
                    payload={
                        "room_name": current_room.name,
                    },
                )
            else:
                g.gencache.set("current_robin_room", current_room.id)

    amqp.consume_items("robin_waitinglist_q", process_waitinglist)
def run_waitinglist():
    @g.stats.amqp_processor("robin_waitinglist_q")
    def process_waitinglist(msg):
        user_id36 = msg.body
        user = Account._byID36(user_id36, data=True, stale=True)
        if RobinRoom.get_room_for_user(user):
            print "%s already in room" % user.name
            return

        with g.make_lock("robin_room", "global"):
            current_room_id = g.cache.get("current_robin_room")
            if not current_room_id:
                current_room = make_new_room()
            else:
                try:
                    current_room = RobinRoom._byID(current_room_id)
                except tdb_cassandra.NotFoundException:
                    current_room_id = None
                    current_room = make_new_room()

                if not current_room.is_alive or current_room.is_continued:
                    current_room_id = None
                    current_room = make_new_room()

            current_room.add_participants([user])
            print "added %s to %s" % (user.name, current_room.id)

            if current_room_id:
                g.cache.delete("current_robin_room")
                current_room.persist_computed_name()
                websockets.send_broadcast(
                    namespace="/robin/" + current_room.id,
                    type="updated_name",
                    payload={
                        "room_name": current_room.name,
                    },
                )
            else:
                g.cache.set("current_robin_room", current_room.id)

    amqp.consume_items("robin_waitinglist_q", process_waitinglist)
Example #44
0
def process_votes_single(qname, limit=0):
    # limit is taken but ignored for backwards compatibility

    def _handle_vote(msg):
        #assert(len(msgs) == 1)
        r = pickle.loads(msg.body)

        uid, tid, dir, ip, organic, cheater = r
        voter = Account._byID(uid, data=True)
        votee = Thing._by_fullname(tid, data = True)
        if isinstance(votee, Comment):
            update_comment_votes([votee])

        # I don't know how, but somebody is sneaking in votes
        # for subreddits
        if isinstance(votee, (Link, Comment)):
            print (voter, votee, dir, ip, organic, cheater)
            handle_vote(voter, votee, dir, ip, organic,
                        cheater = cheater, foreground=True)

    amqp.consume_items(qname, _handle_vote, verbose = False)
Example #45
0
def consume_vote_queue(queue):
    @g.stats.amqp_processor(queue)
    def process_message(msg):
        timer = g.stats.get_timer("new_voting.%s" % queue)
        timer.start()

        vote_data = json.loads(msg.body)

        user = Account._byID(vote_data.pop("user_id"), data=True)
        thing = Thing._by_fullname(vote_data.pop("thing_fullname"), data=True)

        timer.intermediate("preamble")

        lock_key = "vote-%s-%s" % (user._id36, thing._fullname)
        with g.make_lock("voting", lock_key, timeout=5):
            print "Processing vote by %s on %s %s" % (user, thing, vote_data)

            try:
                vote = Vote(
                    user,
                    thing,
                    direction=vote_data["direction"],
                    date=datetime.utcfromtimestamp(vote_data["date"]),
                    data=vote_data["data"],
                    event_data=vote_data.get("event_data"),
                )
            except TypeError as e:
                # a vote on an invalid type got in the queue, just skip it
                g.log.exception("Invalid type: %r", e.message)
                return

            timer.intermediate("create_vote_obj")

            vote.commit()

            timer.flush()

    amqp.consume_items(queue, process_message, verbose=False)
Example #46
0
def consume_vote_queue(queue):
    @g.stats.amqp_processor(queue)
    def process_message(msg):
        timer = g.stats.get_timer("new_voting.%s" % queue)
        timer.start()

        vote_data = json.loads(msg.body)

        user = Account._byID(vote_data.pop("user_id"), data=True)
        thing = Thing._by_fullname(vote_data.pop("thing_fullname"), data=True)

        timer.intermediate("preamble")

        lock_key = "vote-%s-%s" % (user._id36, thing._fullname)
        with g.make_lock("voting", lock_key, timeout=5):
            print "Processing vote by %s on %s %s" % (user, thing, vote_data)

            try:
                vote = Vote(
                    user,
                    thing,
                    direction=vote_data["direction"],
                    date=datetime.utcfromtimestamp(vote_data["date"]),
                    data=vote_data["data"],
                    event_data=vote_data.get("event_data"),
                )
            except TypeError as e:
                # a vote on an invalid type got in the queue, just skip it
                g.log.error(e.message)
                return

            timer.intermediate("create_vote_obj")

            vote.commit()

            timer.flush()

    amqp.consume_items(queue, process_message, verbose=False)
def process_liveupdate_scraper_q():
    @g.stats.amqp_processor('liveupdate_scraper_q')
    def _handle_q(msg):
        d = json.loads(msg.body)

        try:
            liveupdate = parse_embeds(d['event_id'], d['liveupdate_id'])
        except Exception as e:
            g.log.warning("Failed to scrape %s::%s: %r",
                d["event_id"], d["liveupdate_id"], e)
            return

        if not liveupdate.media_objects:
            return

        payload = {
            "liveupdate_id": "LiveUpdate_" + d['liveupdate_id'],
            "media_embeds": liveupdate.embeds
        }
        send_event_broadcast(d['event_id'],
                             type="embeds_ready",
                             payload=payload)

    amqp.consume_items('liveupdate_scraper_q', _handle_q, verbose=False)
def process_liveupdate_scraper_q():
    @g.stats.amqp_processor('liveupdate_scraper_q')
    def _handle_q(msg):
        d = json.loads(msg.body)

        try:
            liveupdate = parse_embeds(d['event_id'], d['liveupdate_id'])
        except Exception as e:
            g.log.warning("Failed to scrape %s::%s: %r", d["event_id"],
                          d["liveupdate_id"], e)
            return

        if not liveupdate.media_objects:
            return

        payload = {
            "liveupdate_id": "LiveUpdate_" + d['liveupdate_id'],
            "media_embeds": liveupdate.embeds
        }
        send_event_broadcast(d['event_id'],
                             type="embeds_ready",
                             payload=payload)

    amqp.consume_items('liveupdate_scraper_q', _handle_q, verbose=False)
def process_liveupdate_scraper_q():
    @g.stats.amqp_processor('liveupdate_scraper_q')
    def _handle_q(msg):
        d = json.loads(msg.body)

        event_id = d.get("event_id") or d["event_fullname"][len("LiveUpdateEvent_"):]
        liveupdate_id = d["liveupdate_id"]

        try:
            fn = TimeoutFunction(parse_embeds, 10)
            liveupdate = fn(event_id, liveupdate_id)
        except TimeoutFunctionException:
            g.log.warning(
                "Timed out on %s::%s", event_id, liveupdate_id)
            return
        except Exception as e:
            g.log.warning("Failed to scrape %s::%s: %r",
                event_id, liveupdate_id, e)
            return

        if not liveupdate:
            return

        if not liveupdate.embeds and not liveupdate.mobile_embeds:
            return

        payload = {
            "liveupdate_id": "LiveUpdate_" + liveupdate_id,
            "media_embeds": liveupdate.embeds,
            "mobile_embeds": liveupdate.mobile_embeds,
        }
        send_event_broadcast(event_id,
                             type="embeds_ready",
                             payload=payload)

    amqp.consume_items('liveupdate_scraper_q', _handle_q, verbose=False)
Example #50
0
def process_adzerk():
    @g.stats.amqp_processor('adzerk_q')
    def _handle_adzerk(msg):
        data = json.loads(msg.body)
        g.log.debug('data: %s' % data)

        action = data.get('action')

        if action == 'deactivate_orphaned_flight':
            _deactivate_orphaned_flight(data['flight'])
            return

        link = Link._by_fullname(data['link'], data=True)
        if data['campaign']:
            campaign = PromoCampaign._by_fullname(data['campaign'], data=True)
        else:
            campaign = None

        if action == 'update_adzerk':
            _update_adzerk(link, campaign)
        elif action == 'deactivate_overdelivered':
            _deactivate_overdelivered(link, campaign)

    amqp.consume_items('adzerk_q', _handle_adzerk, verbose=False)
Example #51
0
def process_flair():
    amqp.consume_items('buttonflair_q', update_flairs)
Example #52
0
def consume_comment_vote_queue(qname="vote_comment_q"):
    @g.stats.amqp_processor(qname)
    def process_message(msg):
        from r2.lib.db.queries import (
            add_queries,
            add_to_commentstree_q,
            get_comments,
        )

        vote_data = json.loads(msg.body)
        hook = hooks.get_hook('vote.validate_vote_data')
        if hook.call_until_return(msg=msg, vote_data=vote_data) is False:
            # Corrupt records in the queue. Ignore them.
            print "Ignoring invalid vote by %s on %s %s" % (
                    vote_data.get('user_id', '<unknown>'),
                    vote_data.get('thing_fullname', '<unknown>'),
                    vote_data)
            return

        timer = g.stats.get_timer("comment_vote_processor")
        timer.start()

        user = Account._byID(vote_data.pop("user_id"))
        comment = Comment._by_fullname(vote_data.pop("thing_fullname"))

        print "Processing vote by %s on %s %s" % (user, comment, vote_data)

        try:
            vote = Vote(
                user,
                comment,
                direction=vote_data["direction"],
                date=datetime.utcfromtimestamp(vote_data["date"]),
                data=vote_data["data"],
                event_data=vote_data.get("event_data"),
            )
        except TypeError as e:
            # a vote on an invalid type got in the queue, just skip it
            g.log.exception("Invalid type: %r", e.message)
            return

        vote.commit()
        timer.intermediate("create_vote_object")

        vote_valid = vote.is_automatic_initial_vote or vote.effects.affects_score
        comment_valid = not (comment._spam or comment._deleted)
        if vote_valid and comment_valid:
            author = Account._byID(comment.author_id)
            add_queries(
                queries=[get_comments(author, sort, 'all') for sort in SORTS],
                insert_items=comment,
            )
            timer.intermediate("author_queries")

            # update the score periodically when a comment has many votes
            update_threshold = g.live_config['comment_vote_update_threshold']
            update_period = g.live_config['comment_vote_update_period']
            num_votes = comment.num_votes
            if num_votes <= update_threshold or num_votes % update_period == 0:
                add_to_commentstree_q(comment)

        timer.stop()
        timer.flush()

    amqp.consume_items(qname, process_message, verbose=False)
Example #53
0
def run(streamfile=None, verbose=False):
    if streamfile:
        stream_fp = open(streamfile, "a")
    else:
        stream_fp = None

    def streamlog(msg, important=False):
        if stream_fp:
            stream_fp.write(msg + "\n")
            stream_fp.flush()
        if important:
            print msg

    def add_timestamps(d):
        d['hms'] = d['time'].strftime("%H:%M:%S")

        d['occ'] = "<%s:%s, pid=%-5s, %s>" % (
            d['host'], d['port'], d['pid'],
            d['time'].strftime("%Y-%m-%d %H:%M:%S"))

    def limited_append(l, item):
        if len(l) >= 25:
            l.pop(12)
        l.append(item)

    def log_exception(d, daystring):
        exc_desc = d['exception_desc']
        exc_type = d['exception_type']

        exc_str = "%s: %s" % (exc_type, exc_desc)

        add_timestamps(d)

        tb = []

        key_material = exc_type
        pretty_lines = []

        make_lock_seen = False
        flaky_db_seen = False
        cassandra_seen = False

        for tpl in d['traceback']:
            tb.append(tpl)
            filename, lineno, funcname, text = tpl
            if text is None:
                pass
            elif (text.startswith("with g.make_lock(")
                  or text.startswith("with make_lock(")):
                make_lock_seen = True
            elif (text.startswith(
                    "(ProgrammingError) server closed the connection")):
                flaky_db_seen = True
            if '/cassandra/' in filename:
                cassandra_seen = True
            key_material += "%s %s " % (filename, funcname)
            pretty_lines.append("%s:%s: %s()" % (filename, lineno, funcname))
            pretty_lines.append("    %s" % text)

        if exc_desc.startswith("QueuePool limit of size"):
            fingerprint = "QueuePool_overflow"
        elif exc_desc.startswith("error 2 from memcached_get: HOSTNAME "):
            fingerprint = "memcache_suckitude"
        elif exc_type == "TimeoutExpired" and make_lock_seen:
            fingerprint = "make_lock_timeout"
        elif exc_desc.startswith("(OperationalError) FATAL: the database " +
                                 "system is in recovery mode"):
            fingerprint = "recovering_db"
        elif exc_desc.startswith("(OperationalError) could not connect " +
                                 "to server"):
            fingerprint = "unconnectable_db"
        elif exc_desc.startswith("(OperationalError) server closed the " +
                                 "connection unexpectedly"):
            fingerprint = "flaky_db_op"
        elif exc_type == "ProgrammingError" and flaky_db_seen:
            fingerprint = "flaky_db_prog"
            # SQLAlchemy includes the entire query in the exception
            # description which can sometimes be gigantic, in the case of
            # SELECTs. Get rid of it.
            select_pos = exc_str.find("SELECT")
            if select_pos > 0:
                exc_str = exc_str[pos]
        elif exc_type == "NoServerAvailable":
            fingerprint = "cassandra_suckitude"
        elif exc_type == "TimedOutException" and cassandra_seen:
            fingerprint = "cassandra_suckitude #2"
        else:
            fingerprint = md5(key_material).hexdigest()

        nickname_key = "error_nickname-" + fingerprint
        status_key = "error_status-" + fingerprint

        nickname = g.hardcache.get(nickname_key)

        if nickname is None:
            nickname = '"%s" Exception' % randword().capitalize()
            news = ("A new kind of thing just happened! " +
                    "I'm going to call it a %s\n\n" % nickname)

            news += "Where and when: %s\n\n" % d['occ']
            news += "Traceback:\n"
            news += "\n".join(pretty_lines)
            news += exc_str
            news += "\n"

            emailer.nerds_email(news, "Exception Watcher")

            g.hardcache.set(nickname_key, nickname, 86400 * 365)
            g.hardcache.set(status_key, "new", 86400)

        if g.hardcache.get(status_key) == "fixed":
            g.hardcache.set(status_key, "new", 86400)
            news = "This was marked as fixed: %s\n" % nickname
            news += "But it just occurred, so I'm marking it new again."
            emailer.nerds_email(news, "Exception Watcher")

        err_key = "-".join(["error", daystring, fingerprint])

        existing = g.hardcache.get(err_key)

        if not existing:
            existing = dict(exception=exc_str, traceback=tb, occurrences=[])

        limited_append(existing['occurrences'], d['occ'])

        g.hardcache.set(err_key, existing, 7 * 86400)

        streamlog("%s [X] %-70s" % (d['hms'], nickname), verbose)

    def log_text(d, daystring):
        add_timestamps(d)
        char = d['level'][0].upper()
        streamlog("%s [%s] %r" % (d['hms'], char, d['text']), verbose)
        logclass_key = "logclass-" + d['classification']

        if not g.hardcache.get(logclass_key):
            g.hardcache.set(logclass_key, True, 86400 * 90)

            if d['level'] != 'debug':
                news = "The code just generated a [%s] message.\n" % \
                       d['classification']
                news += "I don't remember ever seeing one of those before.\n"
                news += "\n"
                news += "It happened on: %s\n" % d['occ']
                news += "The log level was: %s\n" % d['level']
                news += "The complete text was:\n"
                news += repr(d['text'])
                emailer.nerds_email(news, "reddit secretary")

        occ_key = "-".join(
            ["logtext", daystring, d['level'], d['classification']])

        occurrences = g.hardcache.get(occ_key)

        if occurrences is None:
            occurrences = []

        d2 = {}

        d2['occ'] = d['occ']
        d2['text'] = repr(d['text'])

        limited_append(occurrences, d2)
        g.hardcache.set(occ_key, occurrences, 86400 * 7)

    def myfunc(msg):
        daystring = datetime.now(g.display_tz).strftime("%Y/%m/%d")

        try:
            d = pickle.loads(msg.body)
        except TypeError:
            streamlog("wtf is %r" % msg.body, True)
            return

        if not 'type' in d:
            streamlog("wtf is %r" % d, True)
        elif d['type'] == 'exception':
            try:
                log_exception(d, daystring)
            except Exception as e:
                print "Error in log_exception(): %r" % e
        elif d['type'] == 'text':
            try:
                log_text(d, daystring)
            except Exception as e:
                print "Error in log_text(): %r" % e
        else:
            streamlog("wtf is %r" % d['type'], True)

    amqp.consume_items(q, myfunc, verbose=verbose)
Example #54
0
def run(streamfile=None, verbose=False):
    if streamfile:
        stream_fp = open(streamfile, "a")
    else:
        stream_fp = None

    def streamlog(msg, important=False):
        if stream_fp:
            stream_fp.write(msg + "\n")
            stream_fp.flush()
        if important:
            print msg

    def add_timestamps (d):
        d['hms'] = d['time'].strftime("%H:%M:%S")

        d['occ'] = "<%s:%s, pid=%-5s, %s>" % (d['host'], d['port'], d['pid'],
                                      d['time'].strftime("%Y-%m-%d %H:%M:%S"))

    def limited_append(l, item):
        if len(l) >= 25:
            l.pop(12)
        l.append(item)

    def log_exception(d, daystring):
        exc_desc = d['exception_desc']
        exc_type = d['exception_type']

        exc_str = "%s: %s" % (exc_type, exc_desc)

        add_timestamps(d)

        tb = []

        key_material = exc_type
        pretty_lines = []

        make_lock_seen = False
        flaky_db_seen = False
        cassandra_seen = False

        for tpl in d['traceback']:
            tb.append(tpl)
            filename, lineno, funcname, text = tpl
            if text is None:
                pass
            elif (text.startswith("with g.make_lock(") or
                  text.startswith("with make_lock(")):
                make_lock_seen = True
            elif (text.startswith("(ProgrammingError) server closed the connection")):
                flaky_db_seen = True
            if '/cassandra/' in filename.lower():
                cassandra_seen = True
            if '/pycassa/' in filename.lower():
                cassandra_seen = True
            key_material += "%s %s " % (filename, funcname)
            pretty_lines.append ("%s:%s: %s()" % (filename, lineno, funcname))
            pretty_lines.append ("    %s" % text)

        if exc_desc.startswith("QueuePool limit of size"):
            fingerprint = "QueuePool_overflow"
        elif exc_desc.startswith("error 2 from memcached_get: HOSTNAME "):
            fingerprint = "memcache_suckitude"
        elif exc_type == "TimeoutExpired" and make_lock_seen:
            fingerprint = "make_lock_timeout"
        elif exc_desc.startswith("(OperationalError) FATAL: the database " +
                                 "system is in recovery mode"):
            fingerprint = "recovering_db"
        elif exc_desc.startswith("(OperationalError) could not connect " +
                                 "to server"):
            fingerprint = "unconnectable_db"
        elif exc_desc.startswith("(OperationalError) server closed the " +
                                 "connection unexpectedly"):
            fingerprint = "flaky_db_op"
        elif cassandra_seen:
            fingerprint = "something's wrong with cassandra"
        else:
            fingerprint = md5(key_material).hexdigest()

        nickname_key = "error_nickname-" + fingerprint
        status_key = "error_status-" + fingerprint

        nickname = g.hardcache.get(nickname_key)

        if nickname is None:
            nickname = '"%s" Exception' % randword().capitalize()
            news = ("A new kind of thing just happened! " +
                    "I'm going to call it a %s\n\n" % nickname)

            news += "Where and when: %s\n\n" % d['occ']
            news += "Traceback:\n"
            news += "\n".join(pretty_lines)
            news += exc_str
            news += "\n"

            emailer.nerds_email(news, "Exception Watcher")

            g.hardcache.set(nickname_key, nickname, 86400 * 365)
            g.hardcache.set(status_key, "new", 86400)

        if g.hardcache.get(status_key) == "fixed":
            g.hardcache.set(status_key, "new", 86400)
            news = "This was marked as fixed: %s\n" % nickname
            news += "But it just occurred, so I'm marking it new again."
            emailer.nerds_email(news, "Exception Watcher")

        err_key = "-".join(["error", daystring, fingerprint])

        existing = g.hardcache.get(err_key)

        if not existing:
            existing = dict(exception=exc_str, traceback=tb, occurrences=[])

        existing.setdefault('times_seen', 0)
        existing['times_seen'] += 1

        limited_append(existing['occurrences'], d['occ'])

        g.hardcache.set(err_key, existing, 7 * 86400)

        streamlog ("%s [X] %-70s" % (d['hms'], nickname), verbose)

    def log_text(d, daystring):
        add_timestamps(d)
        char = d['level'][0].upper()
        streamlog ("%s [%s] %r" % (d['hms'], char, d['text']), verbose)
        logclass_key = "logclass-" + d['classification']

        if not g.hardcache.get(logclass_key):
            g.hardcache.set(logclass_key, True, 86400 * 90)

            if d['level'] != 'debug':
                news = "The code just generated a [%s] message.\n" % \
                       d['classification']
                news += "I don't remember ever seeing one of those before.\n"
                news += "\n"
                news += "It happened on: %s\n" % d['occ']
                news += "The log level was: %s\n" % d['level']
                news += "The complete text was:\n"
                news += repr(d['text'])
                emailer.nerds_email (news, "reddit secretary")

        occ_key = "-".join(["logtext", daystring,
                            d['level'], d['classification']])

        occurrences = g.hardcache.get(occ_key)

        if occurrences is None:
            occurrences = []

        d2 = {}

        d2['occ'] = d['occ']
        d2['text'] = repr(d['text'])

        limited_append(occurrences, d2)
        g.hardcache.set(occ_key, occurrences, 86400 * 7)

    def myfunc(msg):
        daystring = datetime.now(g.display_tz).strftime("%Y/%m/%d")

        try:
            d = pickle.loads(msg.body)
        except TypeError:
            streamlog ("wtf is %r" % msg.body, True)
            return

        if not 'type' in d:
            streamlog ("wtf is %r" % d, True)
        elif d['type'] == 'exception':
            try:
                log_exception(d, daystring)
            except Exception as e:
                print "Error in log_exception(): %r" % e
        elif d['type'] == 'text':
            try:
                log_text(d, daystring)
            except Exception as e:
                print "Error in log_text(): %r" % e
        else:
            streamlog ("wtf is %r" % d['type'], True)

    amqp.consume_items(q, myfunc, verbose=verbose)
Example #55
0
def consume_comment_vote_queue(qname="vote_comment_q"):
    @g.stats.amqp_processor(qname)
    def process_message(msg):
        from r2.lib.comment_tree import write_comment_scores
        from r2.lib.db.queries import (
            add_queries,
            add_to_commentstree_q,
            get_comments,
        )
        from r2.models.builder import get_active_sort_orders_for_link

        vote_data = json.loads(msg.body)
        hook = hooks.get_hook('vote.validate_vote_data')
        if hook.call_until_return(msg=msg, vote_data=vote_data) is False:
            # Corrupt records in the queue. Ignore them.
            print "Ignoring invalid vote by %s on %s %s" % (
                vote_data.get('user_id', '<unknown>'),
                vote_data.get('thing_fullname', '<unknown>'), vote_data)
            return

        timer = g.stats.get_timer("comment_vote_processor")
        timer.start()

        user = Account._byID(vote_data.pop("user_id"))
        comment = Comment._by_fullname(vote_data.pop("thing_fullname"))

        print "Processing vote by %s on %s %s" % (user, comment, vote_data)

        try:
            vote = Vote(
                user,
                comment,
                direction=vote_data["direction"],
                date=datetime.utcfromtimestamp(vote_data["date"]),
                data=vote_data["data"],
                event_data=vote_data.get("event_data"),
            )
        except TypeError as e:
            # a vote on an invalid type got in the queue, just skip it
            g.log.exception("Invalid type: %r", e.message)
            return

        vote.commit()
        timer.intermediate("create_vote_object")

        vote_invalid = (not vote.effects.affects_score
                        and not vote.is_automatic_initial_vote)
        comment_invalid = comment._spam or comment._deleted
        if vote_invalid or comment_invalid:
            timer.stop()
            timer.flush()
            return

        author = Account._byID(comment.author_id)
        add_queries(
            queries=[get_comments(author, sort, 'all') for sort in SORTS],
            insert_items=comment,
        )
        timer.intermediate("author_queries")

        update_threshold = g.live_config['comment_vote_update_threshold']
        update_period = g.live_config['comment_vote_update_period']
        skip_score_update = (comment.num_votes > update_threshold
                             and comment.num_votes % update_period != 0)

        # skip updating scores if this was the automatic initial vote. those
        # updates will be handled by new_comment. Also only update scores
        # periodically once a comment has many votes.
        if not vote.is_automatic_initial_vote and not skip_score_update:
            # check whether this link is using precomputed sorts, if it is
            # we'll need to push an update to commentstree_q
            link = Link._byID(comment.link_id)
            if get_active_sort_orders_for_link(link):
                # send this comment to commentstree_q where we will update
                # CommentScoresByLink, CommentTree (noop), and CommentOrderer
                add_to_commentstree_q(comment)
            else:
                # the link isn't using precomputed sorts, so just update the
                # scores
                write_comment_scores(link, [comment])
                timer.intermediate("update_scores")

        timer.stop()
        timer.flush()

    amqp.consume_items(qname, process_message, verbose=False)
Example #56
0
def process():
    from r2.models import (
        Account,
        Link,
        NotFound,
        PromoCampaign,
    )

    from reddit_dfp.services import (
        creatives_service,
        lineitems_service,
    )

    def _handle_upsert_promotion(payload):
        link = Link._by_fullname(payload["link"], data=True)
        author = Account._byID(link.author_id)

        creatives_service.upsert_creative(author, link)


    def _handle_upsert_campaign(payload):
        link = Link._by_fullname(payload["link"], data=True)
        campaign = PromoCampaign._by_fullname(payload["campaign"], data=True)
        owner = Account._byID(campaign.owner_id)
        author = Account._byID(link.author_id)

        try:
            lineitem = lineitems_service.upsert_lineitem(owner, campaign)
        except ValueError as e:
            g.log.error("unable to upsert lineitem: %s" % e)
            return

        creative = creatives_service.upsert_creative(author, link)

        lineitems_service.associate_with_creative(
            lineitem=lineitem, creative=creative)


    def _handle_deactivate(payload):
        campaign_ids = payload["campaigns"] and payload["campaigns"].split(",")

        if not campaign_ids:
            return

        campaigns = PromoCampaign._by_fullname(campaign_ids, data=True)
        lineitems_service.deactivate(campaign_ids)


    def _handle_activate(payload):
        campaign_ids = payload["campaigns"] and payload["campaigns"].split(",")

        if not campaign_ids:
            return

        campaigns = PromoCampaign._by_fullname(campaign_ids, data=True)

        lineitems_service.activate(campaign_ids)


    def _handle_check_edits(payload):
        existing = Link._by_fullname(payload["link"], data=True)
        creative = creatives_service.get_creative(existing)

        link = utils.dfp_creative_to_link(
            creative, link=Link._by_fullname(payload["link"], data=True))

        link.dfp_checking_edits = False
        link._commit()


    processor = Processor()

    if feature.is_enabled("dfp_selfserve"):
        g.log.debug("dfp enabled, registering %s processors", DFP_QUEUE)
        processor.register("upsert_promotion", _handle_upsert_promotion)
        processor.register("upsert_campaign", _handle_upsert_campaign)
        processor.register("activate", _handle_activate)
        processor.register("deactivate", _handle_deactivate)

    processor.register("check_edits", _handle_check_edits)

    @g.stats.amqp_processor(DFP_QUEUE)
    def _handler(message):
        rate_limit_ends_at = g.cache.get(RATE_LIMIT_ENDS_AT)
        now_utc = datetime.utcnow()

        if rate_limit_ends_at:
            if now_utc > rate_limit_ends_at:
                g.cache.delete(RATE_LIMIT_ENDS_AT)
            else:
                raise RateLimitException("waiting until %s" % rate_limit_ends_at)

        data = json.loads(message.body)
        g.log.debug("processing action: %s" % data)

        action = data.get("action")
        payload = data.get("payload")

        try:
            processor.call(action, payload)
        except RateLimitException as e:
            g.cache.set(RATE_LIMIT_ENDS_AT, datetime.utcnow() + timedelta(minutes=1))
            raise e

    amqp.consume_items(DFP_QUEUE, _handler, verbose=False)