2013-12-16 2 views
0

Поэтому я использую psycopg2, у меня есть простая таблица:psycopg2 иногда возвращает нуль

CREATE TABLE IF NOT EXISTS feed_cache (
    feed_id int REFERENCES feeds(id) UNIQUE, 
    feed_cache text NOT NULL, 
    expire_date timestamp --without time zone 
); 

Я звоню следующий метод и запрос:

@staticmethod 
def get_feed_cache(conn, feed_id): 
    c = conn.cursor() 
    try: 
     sql = 'SELECT feed_cache FROM feed_cache WHERE feed_id=%s AND localtimestamp <= expire_date;' 
     c.execute(sql, (feed_id,)) 
     result = c.fetchone() 
     if result: 
      conn.commit() 
      return result[0] 
     else: 
      print 'DBSELECT.get_feed_cache: %s' % result 
      print 'sql: %s' % (c.mogrify(sql, (feed_id,))) 
    except: 
     conn.rollback() 
     raise 
    finally: 
     c.close() 
    return None 

Я добавил else, чтобы вывести точный sql и результат, который выполняется и возвращается.

Метод get_feed_cache() вызывается из пула потоков подключения к базе данных. Когда метод get_feed_cache() называется «медленным» (~ 1/sec или меньше), результат возвращается как ожидалось, однако при вызове одновременно он будет иногда return None. Я пробовал несколько способов написания этого запроса &.

Некоторые наблюдения:

  1. Если удалить 'И localtimestamp < = expire_date' из запроса, запрос всегда возвращает результат.
  2. Выполнение запроса быстро в последовательном порядке в psql всегда возвращает результат.
  3. После прочтения методов fetch *() класса курсора psycopg они отмечают, что результаты кэшируются для курсора, я предполагаю, что кеш не используется между разными курсорами. http://initd.org/psycopg/docs/faq.html#best-practices
  4. Я попытался использовать функции postgresql now() и current_timestamp с теми же результатами. (Теперь я сознаю часовой пояс аспекта() & current_timestamp)

Условия отметить:

  1. Там никогда не будет случаем, когда не существует значение feed_cache для предоставленного FEED_ID.
  2. Там никогда не будет случай, когда какое-либо значение в таблице feed_cache является NULL
  3. время тестирования я полностью отключить любые & все записи в этой таблице
  4. Я поставил expire_date быть достаточно далеко в будущем все значения такие, что выражение 'AND localtimestamp < = expire_date' всегда будет истинным.

Вот копия & вставили выход из него не возвращаются None:

DBSELECT.get_feed_cache: None 
sql: SELECT feed_cache FROM feed_cache WHERE feed_id=5 AND localtimestamp < expire_date; 

Ну, что в значительной степени это, я не знаю, что происходит. Возможно, я делаю какую-то туманную ошибку, и я просто этого не замечаю! Моя нынешняя гипотеза заключается в том, что она имеет какое-то отношение к psycopg2 и, возможно, к тому, как она кэширует результаты между курсорами. Если курсоры DO разделяют кеш и запросы происходят одновременно, то возможно, что первый курсор извлекает результат, второй курсор видит, что есть кеш одного и того же запроса, поэтому он не выполняется, затем первый курсор закрывает и удаляет кеш, а второй курсор пытается получить кеш теперь null/None. *

При этом psycopg2 заявляет, что он является потокобезопасным для запросов только для чтения, поэтому, если я не интерпретирую их выполнение поточно-безопасных, это не должно быть так.

Благодарим вас за внимание!

* После добавления блокировки потоков для get_feed_cache, приобретая перед созданием курсора и отпуская, прежде чем вернуться, я до сих пор иногда получаю Отсутствует результат

+1

Вы абсолютно уверены, что в таблице нет значений NULL? Что говорит 'SELECT * FROM feed_cache WHERE feed_cache IS NULL'? Я спрашиваю об этом, потому что ваш код кажется прекрасным, и я чувствую, что в ваших данных что-то не так. – fog

+0

SELECT * FROM feed_cache WHERE feed_cache IS NULL; feed_id | feed_cache | expire_date --------- + ------------ + ------------- (0 строк) – lanthica

+0

Извините за форматирование, 0 строк были возвращены. – lanthica

ответ

1

Я думаю, это, возможно, придется делать с тем фактом, что временные метки возвращенными по localtimestamp или current_timestamp фиксируются, когда начинается транзакция, а не при выполнении инструкции. И psycopg управляет транзакциями за вашей спиной до некоторой степени. Таким образом, вы можете получить немного более старую отметку времени.

Вы можете отладить это, установив log_statement = all на свой сервер, а затем наблюдая, когда операторы BEGIN выполняются относительно ваших запросов.

Возможно, вы захотите изучить функцию, например clock_timestamp(), которая чаще всего обновляется за транзакцию. См. http://www.postgresql.org/docs/current/static/functions-datetime.html.

Смежные вопросы