Je suis un peu nouveau dans les bases de données transactionnelles et j'ai rencontré un problème que j'essaie de comprendre.python postgres cursor timestamp issue
J'ai créé une démonstration simple où une connexion de base de données est stockée dans chacun des 5 threads créés par cherrypy. J'ai une méthode qui affiche une table d'horodatages stockés dans la base de données et un bouton pour ajouter un nouvel enregistrement des horodatages.
La table comporte 2 champs, un pour l'horodatage datetime.datetime.now() transmis par python et un pour l'horodatage de la base de données défini par défaut sur NOW().
CREATE TABLE test (given_time timestamp,
default_time timestamp DEFAULT NOW());
J'ai deux méthodes qui interagissent avec la base de données. Le premier va créer un nouveau curseur, insérer un nouveau given_timestamp, valider le curseur et retourner à la page d'index. La deuxième méthode créera un nouveau curseur, sélectionnera les 10 horodatages les plus récents et les retournera à l'appelant.
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())
Je me attends given_time et default_time horodatages être seulement quelques microsecondes les unes des autres. Cependant, je reçois un comportement étrange. Si j'ajoute des horodateurs toutes les quelques secondes, le default_time n'est pas à quelques microsecondes de l'instant donné, mais il est généralement à quelques microsecondes du précédent donné.
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
Pourtant, si j'ajoute une nouvelle timestamp environ une fois par minute, à la fois la given_time et default_time ne sont que quelques microsecondes au large comme prévu. Cependant, après l'envoi du 6ème horodatage (le nombre de threads + 1), le default_time est à quelques microsecondes du premier horodatage 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
Même si je ferme explicitement le curseur après chaque utilisation, il semble que le curseur précédent est encore réutilisé. Comment est-ce possible si je ferme le curseur après que j'en ai fini et que je crée un nouveau curseur à chaque fois? Quelqu'un peut-il expliquer ce qui se passe ici?
plus proche d'une réponse:
J'ai ajouté un cursor.connection.commit() à la méthode get_timestamps et qui me donne maintenant des données précises avec les horodateurs. Quelqu'un peut-il expliquer pourquoi je pourrais avoir besoin d'appeler cursor.connection.commit() alors que tout ce que je fais est un select? Je devine que chaque fois que je reçois un curseur, une transaction commence (ou continue avec une unité de transaction existante, elle est validée). Y a-t-il une meilleure façon de faire cela ou est-ce que je suis coincé en train de commettre chaque fois que je reçois un curseur indépendamment de ce que je fais avec ce curseur?
Merci d'expliquer cela. Je n'ai pas encore testé vos suggestions, mais j'ai accepté votre réponse pour expliquer pourquoi l'horodatage serait incorrect. Cependant, maintenant je me demande s'il y a une manière que je peux créer un curseur sans commencer une transaction. – adam
Vous pouvez définir Psycopg2 au niveau d'isolation de transaction 'ISOLATION_LEVEL_AUTOCOMMIT', qui ne lancera pas de transaction lorsque des commandes sont émises. Je ne sais pas à quel point ce changement serait vaste; cela pourrait casser d'autres requêtes utilisant des transactions. – kquinn