проблема с отметкой времени курсора Python Postgres - PullRequest
5 голосов
/ 17 марта 2009

Я несколько новичок в транзакционных базах данных и столкнулся с проблемой, которую пытаюсь понять.

Я создал простую демонстрацию, где соединение с базой данных хранится в каждом из 5 потоков, созданных cherrypy. У меня есть метод, который отображает таблицу отметок времени, хранящихся в базе данных, и кнопку для добавления новой записи отметок времени.

таблица имеет 2 поля, одно для метки времени datetime.datetime.now (), переданной python, и одно для метки времени базы данных, установленной по умолчанию NOW ().


CREATE TABLE test (given_time timestamp,
                   default_time timestamp DEFAULT NOW());

У меня есть 2 метода, которые взаимодействуют с базой данных. Первый создаст новый курсор, вставит новый Given_timestamp, зафиксирует курсор и вернется на страницу индекса. Второй метод создаст новый курсор, выберет 10 самых последних временных меток и вернет их вызывающей стороне.


import sys
import datetime
import psycopg2
import cherrypy

def connect(thread_index): 
    # Create a connection and store it in the current thread 
    cherrypy.thread_data.db = psycopg2.connect('dbname=timestamps')

# Tell CherryPy to call "connect" for each thread, when it starts up
cherrypy.engine.subscribe('start_thread', connect)

class Root:
    @cherrypy.expose
    def index(self): 
        html = []
        html.append("<html><body>")

        html.append("<table border=1><thead>")
        html.append("<tr><td>Given Time</td><td>Default Time</td></tr>")
        html.append("</thead><tbody>")

        for given, default in self.get_timestamps():
            html.append("<tr><td>%s<td>%s" % (given, default))

        html.append("</tbody>")
        html.append("</table>")

        html.append("<form action='add_timestamp' method='post'>")
        html.append("<input type='submit' value='Add Timestamp'/>")
        html.append("</form>")

        html.append("</body></html>")
        return "\n".join(html)

    @cherrypy.expose
    def add_timestamp(self):
        c = cherrypy.thread_data.db.cursor()
        now = datetime.datetime.now()
        c.execute("insert into test (given_time) values ('%s')" % now)
        c.connection.commit()
        c.close()
        raise cherrypy.HTTPRedirect('/')

    def get_timestamps(self):
        c = cherrypy.thread_data.db.cursor()
        c.execute("select * from test order by given_time desc limit 10")
        records = c.fetchall()
        c.close()
        return records

if __name__ == '__main__':

    cherrypy.config.update({'server.socket_host': '0.0.0.0',
                            'server.socket_port': 8081,
                            'server.thread_pool': 5,
                            'tools.log_headers.on': False,
                            })

    cherrypy.quickstart(Root())

Я бы ожидал, что отметки времени данного времени и значения по умолчанию будут на расстоянии всего лишь нескольких микросекунд друг от друга. Однако я получаю странное поведение. Если я добавляю временные метки каждые несколько секунд, default_time будет не на несколько микросекунд отсчитываться от данного времени, а обычно на несколько микросекунд с предыдущего данного_ времени.

Given Time                  Default Time
2009-03-18 09:31:30.725017  2009-03-18 09:31:25.218871
2009-03-18 09:31:25.198022  2009-03-18 09:31:17.642010
2009-03-18 09:31:17.622439  2009-03-18 09:31:08.266720
2009-03-18 09:31:08.246084  2009-03-18 09:31:01.970120
2009-03-18 09:31:01.950780  2009-03-18 09:30:53.571090
2009-03-18 09:30:53.550952  2009-03-18 09:30:47.260795
2009-03-18 09:30:47.239150  2009-03-18 09:30:41.177318
2009-03-18 09:30:41.151950  2009-03-18 09:30:36.005037
2009-03-18 09:30:35.983541  2009-03-18 09:30:31.666679
2009-03-18 09:30:31.649717  2009-03-18 09:30:28.319693

Тем не менее, если я добавляю новую временную метку примерно раз в минуту, то данный момент и время по умолчанию на несколько микросекунд отключены, как и ожидалось. Однако после отправки 6-й отметки времени (количество потоков + 1) значение default_time составляет несколько микросекунд с момента первой отметки времени данного времени.

Given Time                  Default Time
2009-03-18 09:38:15.906788  2009-03-18 09:33:58.839075
2009-03-18 09:37:19.520227  2009-03-18 09:37:19.520293
2009-03-18 09:36:04.744987  2009-03-18 09:36:04.745039
2009-03-18 09:35:05.958962  2009-03-18 09:35:05.959053
2009-03-18 09:34:10.961227  2009-03-18 09:34:10.961298
2009-03-18 09:33:58.822138  2009-03-18 09:33:55.423485

Даже если я явно закрываю курсор после каждого использования, кажется, что предыдущий курсор все еще используется повторно. Как это возможно, если я закрываю курсор после того, как закончу с ним, и каждый раз создаю новый курсор? Может кто-нибудь объяснить, что здесь происходит?

Ближе к ответу:

Я добавил cursor.connection.commit () в метод get_timestamps, и теперь он дает мне точные данные с отметками времени. Может кто-нибудь объяснить, почему мне нужно вызывать cursor.connection.commit (), когда все, что я делаю, это выбор? Я предполагаю, что каждый раз, когда я получаю курсор, транзакция начинается (или продолжается с существующей единицей транзакции, которую она фиксирует). Есть ли лучший способ сделать это, или я застреваю, фиксируя каждый раз, когда я получаю курсор, независимо от того, что я делаю с этим курсором?

Ответы [ 3 ]

3 голосов
/ 17 марта 2009

Попробуйте вызвать c.close (), как описано в документации к модулю: http://tools.cherrypy.org/wiki/Databases

def add_timestamp(self):
        c = cherrypy.thread_data.db.cursor()
        now = datetime.datetime.now()
        c.execute("insert into test (given_time) values ('%s')" % now)
        c.connection.commit()
        c.close()
        raise cherrypy.HTTPRedirect('/')

def get_timestamps(self):
        c = cherrypy.thread_data.db.cursor()
        c.execute("select * from test order by given_time desc limit 10")
        records = c.fetchall()
        c.close()
        return records
1 голос
/ 20 марта 2009

Чтобы ответить на вопрос, заданный вашими последними изменениями:

В PostgreSQL NOW() является не текущим временем, а временем в начале текущей транзакции . Psycopg2, вероятно, запускает транзакцию неявно для вас, и, поскольку транзакция никогда не закрывается (с помощью коммита или иным образом), отметка времени застревает и устаревает.

Возможные исправления:

  • Часто совершать (глупо, если вы делаете только SELECT)
  • Настройте Psycopg2 для использования другого поведения для автоматического создания транзакций (возможно, сложно разобраться, и повлияет на другие части вашего приложения)
  • Используйте другую функцию отметки времени, такую ​​как statement_timestamp() (не соответствует стандарту SQL, но в остальном идеально подходит для этого сценария)

С инструкция, раздел 9.9.4 , добавлен акцент:

PostgreSQL предоставляет ряд функции, которые возвращают значения, связанные на текущую дату и время. Эти Все стандартные функции SQL возвращают значения основаны на времени начала текущая транзакция:

  • CURRENT_DATE
  • CURRENT_TIME
  • CURRENT_TIMESTAMP
  • CURRENT_TIME(precision)
  • CURRENT_TIMESTAMP(precision)
  • LOCALTIME LOCALTIMESTAMP
  • LOCALTIME(precision)
  • LOCALTIMESTAMP(precision)

CURRENT_TIME и CURRENT_TIMESTAMP доставить значения с часовым поясом; LOCALTIME и LOCALTIMESTAMP доставить значения без часового пояса.

CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME и LOCALTIMESTAMP банка при желании дать точность параметр, который приводит к результату округляться до такого количества дробных цифры в поле секунд. Без параметр точности, результат с полной доступной точностью.

...

Поскольку эти функции возвращают время начала текущей транзакции, их значения не меняются в течение сделка . Это считается особенность: цель состоит в том, чтобы позволить одна транзакция, чтобы иметь непротиворечивое понятие «ток» время, так что несколько модификаций в рамках той же сделки несут та же отметка времени.

Примечание: Другие системы баз данных могут продвигать эти значения чаще.

PostgreSQL также предоставляет функции которые возвращают время начала текущее заявление, а также текущее текущее время в данный момент функция называется. Полный список нестандартных функций времени:

  • now()
  • transaction_timestamp()
  • statement_timestamp()
  • clock_timestamp()
  • timeofday()

now() - это традиционный PostgreSQL эквивалентно CURRENT_TIMESTAMP. transaction_timestamp() также эквивалентно CURRENT_TIMESTAMP, но назван так, чтобы четко отражать, что это возвращается. statement_timestamp() возвращает время начала текущего заявление (точнее, время получения последней команды сообщение от клиента). statement_timestamp() и transaction_timestamp() вернуть то же значение во время первой команды транзакция, но может отличаться во время последующие команды. clock_timestamp() возвращает фактический текущее время и, следовательно, его значение меняется даже в пределах одного SQL команда. timeofday() является историческим Функция PostgreSQL. подобно clock_timestamp(), возвращает текущее время, но как форматированная текстовая строка, а не временная метка со значением часового пояса.

0 голосов
/ 19 марта 2009

Я добавил коммит в метод, который выбирает метки времени, и это решило проблему.

 def get_timestamps(self):
    c = cherrypy.thread_data.db.cursor()
    c.execute("select * from test order by given_time desc limit 10")
    records = c.fetchall()
    c.connection.commit()  # Adding this line fixes the timestamp issue
    c.close()
    return records

Может кто-нибудь объяснить, почему мне нужно вызывать cursor.connection.commit (), когда все, что я делаю, это выбор?

...