Jestem trochę nowy w transakcyjnych bazach danych i natrafiłem na problem, który próbuję zrozumieć.python postgres kursor znacznik czasu wydania
Stworzyłem prostą demonstrację, w której połączenie z bazą danych jest przechowywane wewnątrz każdego z 5 wątków stworzonych przez cherrypy. Mam metodę, która wyświetla tabelę znaczników czasowych przechowywanych w bazie danych i przycisk, aby dodać nowy rekord znaczników czasu.
tabela ma 2 pola, jeden dla datetime.datetime.now() znacznik czasu przekazany przez pythona i jeden dla znacznika bazy danych ustawiony na domyślny TERAZ().
CREATE TABLE test (given_time timestamp,
default_time timestamp DEFAULT NOW());
Mam 2 metody interakcji z bazą danych. Pierwszy utworzy nowy kursor, wstawi nową given_timestamp, zatwierdzić kursor i powróci na stronę indeksu. Druga metoda utworzy nowy kursor, wybierz 10 ostatnich znaczników czasu i zwróć je do osoby dzwoniącej.
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())
Spodziewałbym się, że znaczniki czasu given_time i default_time są od siebie oddalone o kilka mikrosekund. Jednak dostaję dziwne zachowanie. Jeśli dodaję sygnatury czasowe co kilka sekund, parametr default_time nie jest o kilka mikrosekund wyłączony z określonego czasu, ale zwykle jest o kilka mikrosekund wyłączony z poprzedniego 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
Jednak, jeśli mogę dodać nowy znacznik czasu mniej więcej raz na minutę, zarówno given_time i default_time tylko kilka mikrosekund off, jak oczekiwano. Jednak po przesłaniu szóstego znacznika czasu (liczba wątków + 1) wartość default_time jest o kilka mikrosekund dłuższa od pierwszego znacznika date_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
chociaż jestem jawnie zamykając kursor po każdym użyciu, wydaje się, że poprzedni kursor jest nadal do ponownego wykorzystania. Jak to możliwe, jeśli zamykam kursor po tym, jak skończę i za każdym razem tworzę nowy kursor? Czy ktoś może mi wyjaśnić, co tu się dzieje?
Bliżej odpowiedź:
Dodałem cursor.connection.commit() metody get_timestamps i że teraz daje mi dokładnych danych o znacznikach czasu. Czy ktoś może wyjaśnić, dlaczego mógłbym potrzebować wywołać cursor.connection.commit(), gdy wszystko, co robię, jest select? Zgaduję, że za każdym razem, gdy dostaję kursor, rozpoczyna się transakcja (lub kontynuowana z istniejącą jednostką transakcyjną, która zostaje zatwierdzona). Czy istnieje lepszy sposób to zrobić, czy też utknąłem zobowiązujące za każdym razem, gdy uzyskać kursor niezależnie od tego, co robię z tego kursora?
Dziękuję za wyjaśnienie tego. Nie testowałem jeszcze twoich sugestii, ale przyjąłem twoją odpowiedź za wykonanie zadania wyjaśnienia, dlaczego znacznik czasu byłby niepoprawny. Jednak teraz zastanawiam się, czy istnieje sposób, w jaki mogę utworzyć kursor bez rozpoczynania transakcji. – adam
Możesz ustawić Psycopg2 na poziomie izolacji transakcji 'ISOLATION_LEVEL_AUTOCOMMIT', który nie rozpocznie transakcji po wydaniu komend. Nie wiem, jak dalece byłyby to zmiany; może to spowodować złamanie innych zapytań, które wykorzystują transakcje. – kquinn