1

我现在被屏蔽了。

问题是这样的:我有一个存储在内存缓存中的书籍列表的“视图”。如果我添加一本新书,我想将新书添加到存储书籍列表的 memcache 变量中。更新和删除也是如此。

您可以说:“您可以刷新密钥并再次收集所有数据”。但是由于最终的一致性,当我添加并立即查询新数据时,新数据并不存在。

你可以说:“使用祖先来避免最终的一致性”。书是根实体,为了性能,在这个级别使用祖先不是一个好主意。

因此,我们的想法是尽可能少地从数据存储中读取数据,并同步 memcache。

现在我的代码不起作用:

class Book(ndb.Model):
    """ A Book """
    title = ndb.StringProperty(required=True)
    author = ndb.StringProperty(required=True)

    @classmethod
    def getAll(cls):
        key = 'books'
        books = memcache.get(key)
        if books is None:
            books = list(Book.query().order(Book.title).fetch(100))
            if not memcache.set(key, books):
                logging.error('Memcache set failed for key %s.', key)
        else:
            logging.info('Memcache hit for key %s.', key)
        return books

    @classmethod
    def addMemcache(cls, newdata):
        mylist = memcache.get('books')
        if mylist:
            mylist.insert(0, newdata)
            if not memcache.set('books', mylist):
                logging.error('Memcache set failed for key books.')

    # This saves the data comming from the form
    @classmethod
    def save(cls, **kwargs):
        book = Book(title=kwargs['title'],
                    author=kwargs['author']
               )
        # Save
        book.put()
        # Add to memcache for this key
        logging.info('Adding to Memcache for key books.')
        cls.addMemcache([book.title, book.author])
        return book

现在我只是在列表的开头插入。我的代码的问题是,当我添加到 memcache 时,我缺少某种属性,因为 jinja 模板说“UndefinedError: 'list object' has no attribute 'title'”,当试图表示这一行时:

<td>{{ line.title[:40]|escape }}</td>

这很明显,因为它是一个列表,而不是具有属性的对象。但是为什么当我在函数 getAll() 的列表中转换对象时它会起作用,使 books = list(the_query)?

我的其他问题将是如何修改特定的书(在这种情况下,我可以刷新 de memcache 并再次阅读,因为我认为没有最终的一致性问题)以及如何删除(如果 2,如何识别列表中的唯一元素书名相同)。

有什么建议吗?或者我必须更改我的解决方案来解决 memcache 同步的问题吗?

4

2 回答 2

3

设置 memcache 值时,您正在做两件不同的事情。在getAll中,在缓存未命中时,您执行memcache.set(key, books)的操作books是 Book 实例列表。但是在addMemcache( 调用时,save)您插入了一个列表列表,其中内部列表是书名和作者。所以正如您所注意到的,当您从缓存中获取值时,它们是实例和列表的混合。

似乎保存中的行应该是:

cls.addMemcache(book)

这样您就可以始终将 Book 实例设置为缓存。

(另请注意,我可能会创建addMemcache一个普通的实例方法而不是一个类方法,这会添加self到内存缓存列表中。并且在保存时,最好实例化cls而不是显式调用Book,以防万一您曾经子类化。)

于 2013-05-22T10:24:01.703 回答
1

在@DanielRoseman 的帮助下,我得到了问题的最终解决方案。

我只想在这里为其他感兴趣的“堆垛机”留下完整的解决方案。它包括对 memcache 的添加、编辑和删除元素,这些元素现在正在运行。

# These classes define the data objects to store in AppEngine's data store.
class Book(ndb.Model):
    """ A Book """
    title = ndb.StringProperty(required=True)
    author = ndb.StringProperty(required=True)
    deleted = ndb.BooleanProperty(default=False)

    MEMCACHE_TIMEOUT = 0

    # Key to use in memcache for the list of all books
    @staticmethod
    def book_memkey(key='book_list'):
        return str(key)

    # Search all
    @classmethod
    def get_all(cls):
        key = cls.book_memkey()
        books = memcache.get(key)
        if books is None:
            books = list(Book.query().order(Book.title).fetch(100))
            if not memcache.set(key, books, cls.MEMCACHE_TIMEOUT):
                logging.error('Memcache set failed for key %s.', key)
        else:
            logging.info('Memcache hit for key %s.', key)
        return books

    # Save a Book and return it
    @classmethod
    def save(cls, **kwargs):
        book = cls(title=kwargs['title'],
                   author=kwargs['author']
                  )
        book.put()
        # Modify memcache for this key
        book.add_to_memcache()
        return book

    # ------------------------
    # Methods for the instance
    # ------------------------

    # Add a new element to memcache
    def add_to_memcache(self):
        data = memcache.get(self.book_memkey())
        if data:
            logging.info('Adding to Memcache for key %s.', self.book_memkey())
            data.insert(0, self)
            if not memcache.set(self.book_memkey(), data, self.MEMCACHE_TIMEOUT):
                logging.error('Memcache set failed for key %s.', self.book_memkey())

    # Remove an element from memcache
    def del_from_memcache(self):
        data = memcache.get(self.book_memkey())
        if data:
            logging.info('Removing from Memcache for key %s.', self.book_memkey())
            try:
                # Search the object in the list
                element = filter(lambda idx: idx.key == self.key, data)[0]
            except IndexError:
                pass
            else:
                logging.info('Removing element %s.', element)
                data.remove(element)
                if not memcache.set(self.book_memkey(), data, self.MEMCACHE_TIMEOUT):
                    logging.error('Memcache set failed for key %s.', self.book_memkey())

    # Update an element on memcache
    def update_memcache(self):
        data = memcache.get(self.book_memkey())
        if data:
            logging.info('Updating Memcache for key %s.', self.book_memkey())
            try:
                # Search the object in the list
                element = filter(lambda idx: idx.key == self.key, data)[0]
            except IndexError:
                pass
            else:
                logging.info('Updating element %s.', element)
                data[data.index(element)] = self
                if not memcache.set(self.book_memkey(), data, self.MEMCACHE_TIMEOUT):
                    logging.error('Memcache set failed for key %s.', self.book_memkey())

    # Update a chapter
    def update(self, **kwargs):
        if 'title' in kwargs:
            self.title = kwargs['title']
        if 'author' in kwargs:
            self.author = kwargs['author']
        # Save
        self.put()
        self.update_memcache()

    # Delete de book (mark as deleted). Optionally you can assign Value=False to undelete
    def virtual_delete(self, value=True):
        self.deleted = value
        if value:
            self.del_from_memcache()
        else:
            self.add_to_memcache()
        self.put()
于 2013-05-23T06:53:15.473 回答