2013-12-16 58 views
0

所以我使用的psycopg2,我有一個簡單的表:psycopg2偶爾返回null

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()方法被稱爲「slowishly」(〜1/sec或更少)時,結果會按預期返回,但當同時調用時,它將會偶爾返回無。我已經嘗試了多種方法來編寫這個查詢&方法。

一些觀察:

  1. 如果我刪除 '和LOCALTIMESTAMP < = EXPIRE_DATE' 從查詢,查詢總是返回的結果。
  2. 在psql中以串行方式快速執行查詢總是返回一個結果。
  3. 閱讀了psycopg的遊標類的fetch *()方法後,他們注意到遊標的緩存結果,我假設緩存不是在不同的遊標之間共享的。 http://initd.org/psycopg/docs/faq.html#best-practices
  4. 我已經嘗試使用postgresql的now()和current_timestamp函數獲得相同的結果。 (我知道的時區方面的NOW()& CURRENT_TIMESTAMP)

條件需要注意:

  1. 永遠不會有這樣的情況:沒有一個提供FEED_ID一個feed_cache值。
  2. 永遠不會有這樣的情況:在feed_cache表中的任何值爲NULL
  3. 測試時我已經完全禁止任何&所有寫入該表
  4. 我在將來足夠遠的設置EXPIRE_DATE成爲所有表達式'AND localtimestamp < = expire_date'將始終爲真。

這裏是它返回無副本&粘貼輸出:

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

嗯,這是相當多了,我不知道發生了什麼事。也許我正在犯一些非常愚蠢的錯誤,我只是沒有注意到它! 我目前的猜測是它與psycopg2有關,也許它是在遊標之間緩存結果的方式。如果遊標DO共享緩存並且查詢幾乎同時發生,那麼第一個遊標可能會提取結果,第二個遊標可能會看到相同查詢的緩存,因此它不會執行,那麼第一個遊標關閉並刪除緩存,第二個遊標嘗試獲取現在爲空/無緩存。 *

也就是說,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 rows) – lanthica

+0

對不起格式化,0行被退回。 – lanthica

回答

1

我想,這可能與該時間戳返回的事實做通過localtimestampcurrent_timestamp在交易開始時是固定的,而不是當您運行該語句時。 psycopg在某種程度上管理你背後的交易。所以你可能會得到一個稍微老一點的時間戳。

您可以通過在服務器中設置log_statement = all進行調試,然後觀察相對於您的查詢執行的BEGIN語句的執行情況。

您可能想要使用諸如clock_timestamp()之類的函數進行研究,該函數每個事務更經常更新。見http://www.postgresql.org/docs/current/static/functions-datetime.html