Soy algo nuevo en las bases de datos transaccionales y he encontrado un problema que intento comprender.python postgres cursor timestamp número
He creado una demostración simple donde se almacena una conexión de base de datos dentro de cada uno de los 5 subprocesos creados por cherrypy. Tengo un método que muestra una tabla de marcas de tiempo almacenadas en la base de datos y un botón para agregar un nuevo registro de marcas de tiempo.
la tabla tiene 2 campos, uno para datetime.datetime.now() timestamp pasado por python y otro para la marca de tiempo de la base de datos establecida en forma predeterminada AHORA().
CREATE TABLE test (given_time timestamp,
default_time timestamp DEFAULT NOW());
Tengo 2 métodos que interactúan con la base de datos. El primero creará un nuevo cursor, insertará un nuevo given_timestamp, confirmará el cursor y regresará a la página de índice. El segundo método creará un nuevo cursor, seleccionará las 10 marcas de tiempo más recientes y las devolverá a la persona que llama.
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())
Yo esperaría que las marcas de tiempo y given_time default_time sean sólo unos pocos microsegundos separados unos de otros. Sin embargo, estoy obteniendo un comportamiento extraño. Si agrego marcas de tiempo cada pocos segundos, el tiempo predeterminado no está a unos pocos microsegundos del tiempo_dido, pero generalmente está a unos pocos microsegundos del anterior given_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
Sin embargo, si añado una nueva marca de tiempo una vez por minuto, tanto el given_time y default_time son sólo unos pocos microsegundos fuera como se esperaba. Sin embargo, después de enviar la sexta marca de tiempo (el número de subprocesos + 1), el tiempo predeterminado está a unos microsegundos de la primera marca de tiempo given_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
A pesar de que estoy cerrando explícitamente el cursor después de cada uso, se observa que el cursor anterior aún está siendo reutilizado. ¿Cómo es posible si estoy cerrando el cursor después de que termine con él y creando un nuevo cursor cada vez? ¿Alguien puede explicar lo que está pasando aquí?
cerca de una respuesta:
He añadido una cursor.connection.commit() al método get_timestamps y que ahora me da datos precisos con las marcas de tiempo. ¿Alguien puede explicar por qué podría necesitar llamar a cursor.connection.commit() cuando todo lo que hago es una selección? Supongo que cada vez que obtengo un cursor, una transacción comienza (o continúa con una unidad de transacción existente). ¿Hay una manera mejor de hacer esto o estoy atrapado cometiendo cada vez que obtengo un cursor independientemente de lo que hago con ese cursor?
Gracias por explicar esto. Aún no he probado sus sugerencias, pero acepté su respuesta por hacer el mejor trabajo al explicar por qué la marca de tiempo sería incorrecta. Sin embargo, ahora me pregunto si existe una forma de crear un cursor sin iniciar una transacción. – adam
Puede configurar Psycopg2 en el nivel de aislamiento de transacción 'ISOLATION_LEVEL_AUTOCOMMIT', que no iniciará transacciones cuando se emitan comandos. Sin embargo, no sé cuán amplio sería ese cambio; hacerlo podría romper otras consultas que usan transacciones. – kquinn