首页 文章

psycopg2偶尔会返回null

提问于
浏览
0

所以我使用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 回答

  • 1

    我认为这可能与 localtimestampcurrent_timestamp 返回的时间戳在事务开始时是固定的,而不是在运行语句时有关 . psycopg在某种程度上管理你背后的交易 . 所以你可能会得到一个稍微旧的时间戳 .

    您可以通过在服务器中设置 log_statement = all 然后观察相对于查询执行 BEGIN 语句的时间进行调试 .

    您可能希望使用诸如 clock_timestamp() 之类的函数,该函数每次事务更频繁地更新 . 见http://www.postgresql.org/docs/current/static/functions-datetime.html .

相关问题