Таблица поиска GAE несовместима с транзакциями?
Моему приложению Python High Replication Datastore требуется большая таблица поиска от 100000 до 1 000000 записей. Мне нужно иметь возможность предоставить код некоторому методу, который будет возвращать значение, связанное с этим кодом (или None, если нет связи). Например, если моя таблица содержит приемлемые английские слова, я бы хотел, чтобы функция возвращала True, если слово было найдено, и False (или None) в противном случае.
Моя текущая реализация состоит в том, чтобы создать один объект без родителей для каждой записи таблицы, и чтобы этот объект содержал любые связанные данные. Я установил ключ хранилища данных для этой сущности так же, как мой код поиска. (Я помещаю все сущности в их собственное пространство имен, чтобы предотвратить любые конфликты клавиш, но это не существенно для этого вопроса.) Затем я просто вызываю get_by_key_name() в коде и получаю связанные данные.
Проблема в том, что я не могу получить доступ к этим объектам во время транзакции, потому что я буду пытаться охватить группы объектов. Итак, возвращаясь к моему примеру, скажем, я хотел проверить правописание всех слов, используемых в сеансе чата. Я мог получить доступ ко всем сообщениям в чате, потому что я дал бы им общего предка, но я не мог получить доступ к своей таблице слов, потому что записи там не содержат родителей. Крайне важно, чтобы я мог ссылаться на таблицу во время транзакций.
Обратите внимание, что моя таблица поиска исправлена или изменяется очень редко. Опять же, это соответствует примеру проверки орфографии.
Одним из решений может быть загрузка всех слов в сеансе чата во время одной транзакции, затем проверка их правописания (сохранение результатов), а затем запуск второй транзакции, которая будет проверять орфографию сохраненных результатов. Но это не только неэффективно, но и между транзакциями может быть добавлен сеанс чата. Это похоже на неуклюжее решение.
В идеале я хотел бы сказать GAE, что таблица поиска является неизменной, и что из-за этого я должен иметь возможность запрашивать ее, не жалуясь на охват групп объектов в транзакции. Однако я не вижу способа сделать это.
Хранение записей таблицы в memcache заманчиво, но это тоже имеет проблемы. Это большой объем данных, но более проблематичным является то, что если GAE загрузит запись memcache, я не смогу перезагрузить ее во время транзакции.
Кто-нибудь знает подходящую реализацию для больших глобальных таблиц поиска?
Пожалуйста, поймите, что я не ищу веб-сервис для проверки орфографии или что-то подобное. Я использую поиск слов в качестве примера, только чтобы прояснить этот вопрос, и я надеюсь на общее решение для любого вида больших таблиц поиска.
2 ответа
Если вы можете, попробуйте и поместите данные в память экземпляра. Если он не помещается в памяти экземпляра, у вас есть несколько доступных вариантов.
Вы можете хранить данные в файле ресурсов, который вы загружаете вместе с приложением, если оно меняется нечасто, и получать к нему доступ с диска. Это предполагает, что вы можете создать структуру данных, которая позволяет легко осуществлять поиск на диске - фактически вы реализуете свою собственную таблицу только для чтения на диске.
Аналогично, если он слишком велик, чтобы его можно было использовать в качестве статического ресурса, вы можете использовать тот же подход, что и выше, но хранить данные в хранилище больших двоичных объектов.
Если ваши данные обязательно должны быть в хранилище данных, вам может потребоваться эмулировать ваши собственные транзакции чтения-изменения-записи. Добавьте свойство 'revision' в свои записи. Чтобы изменить его, извлеките запись (вне транзакции), внесите необходимые изменения, затем внутри транзакции извлеките ее снова, чтобы проверить значение ревизии. Если он не изменился, увеличьте версию своей записи и сохраните ее в хранилище данных.
Обратите внимание, что базовый уровень RPC теоретически поддерживает несколько независимых транзакций (и нетранзакционных операций), но API в настоящее время не предоставляют какого-либо способа доступа к нему из транзакции, за исключением ужасных (и я имею в виду действительно ужасных) хаков, к несчастью.
Один последний вариант: вы можете запустить бэкэнд, предоставляющий больше памяти, предоставляя SpellCheckService и вызывая URLFetch, вызывая его из ваших внешних интерфейсов. Помните, что оперативная память всегда будет намного, намного быстрее, чем любая дисковая опция.
Во-первых, если вы уверены, что пространство имен поможет избежать столкновений клавиш, пришло время сделать шаг назад. Ключ состоит из вида объекта, пространства имен, имени или идентификатора и любых родителей, которые могут быть у объекта. Вполне допустимо, чтобы два разных типа сущностей имели одно и то же имя или идентификатор. Так что если у вас есть, скажем, LookupThingy
с которым вы сопоставляете, и создали каждого участника, указав уникальное имя, ключ не столкнется ни с чем другим.
Что касается задачи, связанной с проверкой орфографии по отношению к непаренной таблице поиска в транзакции, возможно ли сохранить таблицу поиска в коде?
Или вы можете придумать аналогию, которая ближе к тому, что вам нужно? Тот, который мотивирует необходимость поиска в транзакции?