Фильтрация NDB для структурированной собственности Google App Engine

У меня есть модели ndb:

class Product(ndb.Model):
    name = ndb.StringProperty()
    description = ndb.StringProperty()
    code = ndb.StringProperty()

class Category(ndb.Model):
    name = ndb.StringProperty()

class Shop(ndb.Model):
    name = ndb.StringProperty()
    category = ndb.StructuredProperty(Category)
    address = ndb.StringProperty()

class ProductInShop(ndb.Model):
    product = ndb.StructuredProperty(Product)
    shop = ndb.StructuredProperty(Shop)
    price = ndb.FloatProperty()

И в функции мне нужно найти товар в магазине (код товара и ключ магазина в запросе). Если в магазине нет товара (возвращается None), мне нужно его создать. Если он существует, обновите значение цены из запроса. Теперь я делаю запрос с помощью gql.

product_in_shop = ndb.gql(
        'SELECT * FROM ProductInShop WHERE \
product.code = :1 AND shop = :2 LIMIT 1',
        request.code,
        ndb.Key(Shop, request.shop_key).get()
    ).get()

И это работает нормально. Но! Несколько раз product_in_shop = None когда должна быть сущность (я проверял в базе данных). Я попытался сделать еще один запрос для подсчета объектов, и он вернул 0, но было больше, чем 1 объект.

Когда я вижу эту сумку, я могу просто обновить ее (python appcfg.py -A project-name update project-name), и она работает...

Любые идеи, что я делаю неправильно или как это исправить?


comment
Я предполагаю, что это связано с возможной согласованностью. cloud.google.com/datastore/docs/concepts/   -  person Alexander Trakhimenok    schedule 29.04.2016


Ответы (1)


Скорее всего, это Eventual Consistency. При проектировании системы с помощью Cloud Datastore необходимо предусмотреть случай, когда для обновления этих запросов может потребоваться время. Скорее всего, повторное развертывание вашего приложения в этом случае займет достаточно времени, чтобы информация обновилась к тому времени, когда вы снова проверите.

Чтобы гарантировать строгую согласованность, вы захотите организовать свои данные в группы сущностей. Важным соображением для вашей модели данных является то, что внутри EG вы можете запрашивать строго согласованным образом. Однако вы можете обновлять только одну группу сущностей с постоянной скоростью 1 обновление в секунду (хотя это обновление может быть пакетным).

Попытайтесь выровнять эти группы сущностей по естественным границам, чтобы ограничить скорость записи в любую данную группу сущностей по мере масштабирования вашего приложения. Например, у вас может быть одна группа сущностей для каждого пользователя или, в случае вашего приложения, одна группа сущностей для каждого Shop. Если вы поместите все Product для одного Shop в одну группу сущностей, вы всегда сможете перечислить все Product в магазине строго согласованным образом. Вы не сможете увеличить количество людей, добавляющих товары в магазин (вы можете записывать продукты в магазин до одной партии в секунду), но это, вероятно, будет приемлемым ограничением для системы.

Поэтому вместо этого рассмотрим следующий макет:

class Product(ndb.Model):
    name = ndb.StringProperty()
    description = ndb.StringProperty()
    code = ndb.StringProperty()

class Category(ndb.Model):
    name = ndb.StringProperty()

class Shop(ndb.Model):
    name = ndb.StringProperty()
    category = ndb.StructuredProperty(Category)
    address = ndb.StringProperty()

class ProductInShop(ndb.Model):
    product = ndb.StructuredProperty(Product)
    price = ndb.FloatProperty()

Когда вы создаете ProductInShop, вы хотите установить родителя на ключ Shop, частью которого он является. Например:

ProductInShop(parent=ndb.Key(Shop, request.shop_key),
    product=Product(...), price=10.0).put()

Затем вы можете выполнить запрос для одного продукта в данном магазине с определенным кодом:

product_in_shop = ndb.gql(
        'SELECT * FROM ProductInShop WHERE \
product.code = :1 AND ANCESTOR IS :2 LIMIT 1',
        request.code,
        ndb.Key(Shop, request.shop_key)
    ).get()

В качестве примечания: если ваш code уникален для одного продукта, рассмотрите возможность установки идентификатора ProductInShop в качестве кода. Тогда вам не нужно выдавать запрос для этого:

ProductInShop(id=product.code, product=product, price=10.0).put()

Затем вы можете найти свой ProductInShop без запроса с помощью:

product_in_shop = ndb.Key(Shop, request.shop_key, ProductInShop, request.code).get()
person Patrick Costello    schedule 05.05.2016