所以我使用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()方法被调用"slowishly"(~1 /秒或更少)时,结果将按预期返回,但是当并发调用时,它偶尔会返回None . 我尝试了多种编写此查询和方法的方法 .
一些观察:
-
如果从查询中删除'AND localtimestamp <= expire_date',查询始终返回结果 .
-
在psql中以串行方式快速执行查询始终返回结果 .
-
在阅读psycopg 's cursor class they note that the results are cached for the cursor, I' m的fetch *()方法之后,假设不在不同游标之间共享缓存 . http://initd.org/psycopg/docs/faq.html#best-practices
-
我尝试过使用postgresql的now()和current_timestamp函数,结果相同 . (我知道now()和current_timestamp的时区方面)
需要注意的条件:
-
绝不会出现提供的feed_id没有feed_cache值的情况 .
-
不会出现feed_cache表中任何值为NULL的情况
-
测试时我完全禁用了对该表的任何和所有写入
-
我已将expire_date设置为将来所有值都足够远,以使表达式'AND localtimestamp <= expire_date'始终为true .
这是它的复制和粘贴输出返回None:
DBSELECT.get_feed_cache: None
sql: SELECT feed_cache FROM feed_cache WHERE feed_id=5 AND localtimestamp < expire_date;
好吧,'s pretty much it, I'我不确定's going on. Maybe I'是什么造成了一些非常愚蠢的错误,我只是没注意到它!我目前的猜测是它与psycopg2有关,也许它与游标之间的缓存结果有关 . 如果游标共享缓存并且查询几乎同时发生,则第一个游标可能会获取结果,第二个游标看到存在相同查询的缓存,因此它不执行,然后是第一个游标关闭并删除缓存,第二个游标尝试获取一个现在为null / None的缓存 . *
也就是说,psycopg2声明它对于只读查询是线程安全的,所以除非我错过了解释它们的线程安全实现,否则不应该这样 .
感谢您的时间!
*为get_feed_cache添加线程锁之后,在创建游标之前获取并在返回之前释放,我仍然偶尔得到一个None结果
1 回答
我认为这可能与
localtimestamp
或current_timestamp
返回的时间戳在事务开始时是固定的,而不是在运行语句时有关 . psycopg在某种程度上管理你背后的交易 . 所以你可能会得到一个稍微旧的时间戳 .您可以通过在服务器中设置
log_statement = all
然后观察相对于查询执行BEGIN
语句的时间进行调试 .您可能希望使用诸如
clock_timestamp()
之类的函数,该函数每次事务更频繁地更新 . 见http://www.postgresql.org/docs/current/static/functions-datetime.html .