芹菜酸洗不适合Cassandra司机,不能找出根本原因

时间:2016-03-10 17:40:00

标签: python cassandra celery pickle

我遇到了一些我无法理解的行为。我使用Cassandra存储消息对象,并且我使用Celery进行异步拉取并推送到数据库。一切都很好,除了一个Celery任务;使用相同代码/类的其他任务有效。这是代码逻辑的粗略细分:

db_manager = DBManager()

class User(object):
    def __init__(self, user_id):
        ... normal init stuff ...
        self.loader()

    @run_async
    def loader(self):
        ... loads from database if found, otherwise pulls from API ...

    # THIS WORKS
    @celery.task(name='user-to-db', filter=task_method)
    def to_db(self):
         # db_manager is a custom backend that handles relevant db reads, writes, etc.
         db_manager.add('users', self.user_payload)

     # THIS WORKS
     @celery.task(name='load-friends', filter=task_method)
     def load_friends(self):
          # Checks secondary redis index for friends of user
          friends = redis.srandmember('users:the-users-id:friends', self.id, 20)
          if not friends:
               profiles = load_friends_from_api(user_id=self.id)
          else:
               query = "SELECT * FROM keyspace.users WHERE id IN ({friends})".format(friends=friends)
          # Init a User object for every friend
          loaded_friends = [User(friend) for friend in profiles]
          # Returns a class container with all the instances of User(friend), accessible through a class property
          return FriendContainer(self.id, loaded_friends)

     # THIS DOES NOT WORK
     @celery.task(name='get-user-messages', filter=task_method)
     def get_user_messages(self):
          # THIS IS WHERE IT FAILS #
          messages = db_manager.get("SELECT message FROM keyspace.message_timelines WHERE user_id = {user_id}".format(user_id=self.id))
          # THAT LINE ABOVE #

          # Init a message class object for every message payload in database
          msgs = [Message(m, user=self) for m in messages]
          # Returns a message container class holding all the message objects, accessible through a class property
          return MessageContainer(msgs)

这最后一个类方法抛出错误:

File "/usr/local/lib/python2.7/dist-packages/kombu/serialization.py", line 356, in pickle_dumps

return dumper(obj, protocol=pickle_protocol)

EncodeError: Can't pickle <class 'cassandra.io.eventletreactor.message'>: attribute lookup cassandra.io.eventletreactor.message failed

cassandra.io.eventletreactor.message指向Cassandra中的用户定义类型,我将其用作每个用户的消息对象的容器。抛出此错误的行是:

messages = db_manager.get("SELECT message FROM keyspace.message_timelines WHERE user_id = {user_id}".format(user_id=self.id))

这是来自DBManager()的方法:

class DBManager(object):
    ... stuff ...

    def get(self, query):
        # I do some stuff to prepare the query, namely substituting `WHERE this = that` for `WHERE this = ?` to create a Cassandra prepared statement.
        statement = cassandra.prepare(query_prepared)
        # I want these messages as a dict, not the default namedtuple
        cassandra.row_factory = dict_factory
        # User id is parsed out of query
        results = cassandra.execute(statement, (user_id,))
        rows = results.current_rows
        # rows is a list of dicts, no weird class references or anything in there
        return rows

我已经读过Celery任务的课外方法是实验性的,但是我无法弄清楚为什么所有其他方法都使用同一个DBManager实例的任务在工作中。

问题似乎局限于某些问题,用户定义的类型message在Cassandra驱动程序中表现不佳;但是,如果我在Celery任务本身中运行get中的DBManager方法,它就可以。也就是说,如果我复制/粘贴引发错误的代码DBManager.get User.get_user_messages进入DBManager.get,效果很好。如果我尝试从User.get_user_messages内拨打get_user_messages,则会中断。

我无法弄清楚问题所在。我可以做以下所有就好了

  1. 运行get_user_messages方法,不用 Celery,它可以正常运行。
  2. 如果我在Celery任务方法本身中运行get方法代码,则运行DBManager方法 WITH Celery。
  3. 我可以运行其他注册为Celery任务的方法,这些方法指向message中使用Cassandra驱动程序的其他方法,即使是那些将相同的json用户定义类型插入数据库的方法。
  4. 我已经尝试过将所有的事情一路腌制成自己,并以各种方式组合,并且无法重现错误。
  5. 没有尝试过:

    1. 将序列化程序更改为yamldill。数据库有效负载中有一些便利项目不会使用这两个中的任何一个进行序列化。
    2. 使用pickle代替DBManager。看起来这应该可以在不必切换序列化器的情况下工作,因为我可以让各个部件单独工作。
    3. 我可以说它是直接通过Cassandra驱动程序而不是我的str.strip类来运行查询,但我觉得这应该是可以解决的,而我只是遗漏了一些非常非常明显的东西,很明显,我没有看到它。任何关于在哪里寻找的建议都将不胜感激。

      如果相关:Cassandra 3.3,CQL 3.4,DataStax python driver 3.1

1 个答案:

答案 0 :(得分:0)

嗯,我发现了问题,这真的很明显。我想我实际上并没有尝试腌制所有的东西,只是大部分的东西,而且我在凌晨4点的调试昏迷中没有抓住这个。

无论如何,cassandra.row_factory = dict_factory在用户定义的类型上调用时,实际上并未将所有内容作为dict返回。它给出了{'label': message(x='this', y='that')}的字典,其中message是一个名字元组。 Cassandra驱动程序在类实例中动态创建了namedtuple,因此pickle无法找到它。