2009-03-17 20 views
5

Tôi hơi mới đối với cơ sở dữ liệu giao dịch và đã gặp một vấn đề mà tôi đang cố gắng hiểu.python postgres pointer timestamp issue

Tôi đã tạo một cuộc biểu tình đơn giản trong đó kết nối cơ sở dữ liệu được lưu trữ bên trong mỗi trong số 5 luồng được tạo bởi cherrypy. Tôi có một phương pháp hiển thị một bảng các dấu thời gian được lưu trữ trong cơ sở dữ liệu và một nút để thêm một bản ghi thời gian mới.

bảng có 2 trường, một cho dấu thời gian datetime.datetime.now() được chuyển bởi python và một cho dấu thời gian cơ sở dữ liệu được đặt thành mặc định NOW().


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

Tôi có 2 phương pháp tương tác với cơ sở dữ liệu. Việc đầu tiên sẽ tạo ra một con trỏ mới, chèn một given_timestamp mới, cam kết con trỏ và trở về trang chỉ mục. Phương pháp thứ hai sẽ tạo ra một con trỏ mới, chọn 10 dấu thời gian gần đây nhất và trả về dấu thời gian cho người gọi.


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()) 

Tôi hy vọng dấu thời gian given_time và default_time chỉ cách nhau một vài micro giây. Tuy nhiên tôi nhận được một số hành vi kỳ lạ. Nếu tôi thêm dấu thời gian vài giây một lần, default_time không phải là một vài micro giây so với given_time, nhưng thường là một vài micro giây từ trước đó 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 

Tuy nhiên, nếu tôi thêm dấu thời gian mới khoảng một lần một phút, cả thời gian đã cho và thời gian mặc định chỉ là một vài giây như mong đợi. Tuy nhiên, sau khi gửi dấu thời gian thứ 6 (số lượng các chủ đề + 1) default_time là một vài micro giây tắt từ dấu thời gian given_time đầu tiên.

 
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ặc dù tôi rõ ràng đóng con trỏ sau mỗi lần sử dụng, dường như con trỏ trước đó vẫn đang được sử dụng lại. Làm thế nào là có thể nếu tôi đóng con trỏ sau khi tôi hoàn thành nó và tạo một con trỏ mới mỗi lần? Ai đó có thể giải thích những gì đang diễn ra ở đây không?

Closer to một câu trả lời:

Tôi đã thêm một cursor.connection.commit() để phương pháp get_timestamps và bây giờ mang lại cho tôi dữ liệu chính xác với timestamps. Bất cứ ai có thể giải thích tại sao tôi có thể cần phải gọi cursor.connection.commit() khi tất cả tôi đang làm là một lựa chọn? Tôi đoán rằng mỗi khi tôi nhận được một con trỏ, một giao dịch bắt đầu (hoặc tiếp tục với một đơn vị giao dịch hiện tại nó được cam kết). Có cách nào tốt hơn để làm điều này hoặc tôi bị mắc kẹt cam kết mỗi khi tôi nhận được một con trỏ bất kể những gì tôi làm với con trỏ đó?

Trả lời

1

Để giải quyết những câu hỏi được đặt ra bởi chỉnh sửa gần đây nhất của bạn:

Trong PostgreSQL, NOW()không thời điểm hiện tại, nhưng thời gian vào lúc bắt đầu của giao dịch hiện tại. Psycopg2 có lẽ là bắt đầu một giao dịch ngầm cho bạn, và kể từ khi giao dịch không bao giờ bị đóng (bằng một cam kết hay cách khác), dấu thời gian bị 'kẹt' và trở nên cũ.

có thể sửa chữa:

  • Commit thường xuyên (ngớ ngẩn nếu bạn chỉ làm chọn)
  • Thiết lập Psycopg2 sử dụng hành vi khác nhau để tự động tạo ra các giao dịch (có thể là khó khăn để có được quyền, và sẽ ảnh hưởng đến các phần khác của ứng dụng)
  • Sử dụng chức năng dấu thời gian khác, như statement_timestamp() (không tuân thủ tiêu chuẩn SQL, nhưng nếu không hoàn hảo cho trường hợp này)

Từ the manual, section 9.9.4, nhấn mạnh thêm:

PostgreSQL cung cấp một số chức năng mà trở lại giá trị liên quan đến ngày và thời gian hiện tại. Những hàm SQL tiêu chuẩn tất cả trở giá trị dựa trên thời gian bắt đầu của giao dịch hiện tại :

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

CURRENT_TIMECURRENT_TIMESTAMP cung cấp giá trị với múi giờ; LOCALTIMELOCALTIMESTAMP phân phối các giá trị không có múi giờ.

CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIMELOCALTIMESTAMP thể tùy chọn được cung cấp một tham số chính xác , gây hậu quả để được làm tròn với nhiều phân đoạn chữ số trong lĩnh vực giây. Không có tham số chính xác , kết quả là được cung cấp cho độ chính xác có sẵn đầy đủ.

...

Kể từ các chức năng trả lại thời gian bắt đầu giao dịch vãng lai, giá trị của chúng không thay đổi trong suốt giao dịch. Đây được coi là một tính năng : mục đích là cho phép giao dịch đơn lẻ để có khái niệm nhất quán về thời gian "hiện tại" , do đó nhiều sửa đổi trong cùng một giao dịch có cùng dấu thời gian .

Lưu ý: Các hệ thống cơ sở dữ liệu khác có thể nâng cao các giá trị này thường xuyên hơn.

PostgreSQL cũng cung cấp chức năng trả lại thời gian bắt đầu của báo cáo kết quả hiện nay, cũng như thời điểm hiện tại thực tế tại ngay lập tức chức năng được gọi. Danh sách đầy đủ các chức năng thời gian phi SQL tiêu chuẩn là:

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

now() là một PostgreSQL truyền thống tương đương với CURRENT_TIMESTAMP. transaction_timestamp() tương tự như vậy tương đương với CURRENT_TIMESTAMP, nhưng được đặt tên để phản ánh rõ ràng những gì nó trả về. statement_timestamp() trả về thời gian bắt đầu của tuyên bố hiện tại (cụ thể hơn, thời gian khi nhận được lệnh mới nhất tin nhắn từ khách hàng). statement_timestamp()transaction_timestamp() trả về cùng một giá trị trong lệnh đầu tiên của một giao dịch, nhưng có thể khác nhau trong các lệnh tiếp theo. clock_timestamp() trả về số thực hiện thời gian hiện tại và do đó giá trị thay đổi ngay cả trong một lệnh SQL . timeofday() là một hàm lịch sử PostgreSQL. Giống như clock_timestamp(), nó trả về thời gian thực tế hiện tại, nhưng dưới dạng một chuỗi văn bản được định dạng được định dạng thay vì một dấu thời gian với giá trị múi giờ.

+0

Cảm ơn bạn đã giải thích điều này. Tôi chưa thử nghiệm các đề xuất của bạn, nhưng tôi đã chấp nhận câu trả lời của bạn để thực hiện công việc tốt nhất để giải thích lý do tại sao dấu thời gian sẽ không chính xác. Tuy nhiên, bây giờ tôi tự hỏi nếu có một cách tôi có thể tạo ra một con trỏ mà không cần bắt đầu một giao dịch. – adam

+0

Bạn có thể đặt Psycopg2 thành mức cô lập giao dịch 'ISOLATION_LEVEL_AUTOCOMMIT', sẽ không bắt đầu giao dịch khi lệnh được phát hành. Tuy nhiên, tôi không biết sự thay đổi rộng lớn như thế nào; làm như vậy có thể phá vỡ các truy vấn khác sử dụng giao dịch. – kquinn

3

thử gọi c.close() như mô tả trong tài liệu mô-đun: 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 
+0

Tôi vừa thực hiện các thay đổi đó và khởi động lại máy chủ cherrypy và tôi vẫn gặp sự cố tương tự. – adam

+0

Cập nhật: chúng tôi đã chuyển từ postgres 8.0 sang 8.3 và câu trả lời này hiện cũng hoạt động. – adam

0

Tôi đã thêm một cam kết phương pháp mà chọn timestamps và đã giải quyết được vấn đề.

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 

Bất cứ ai có thể giải thích tại sao tôi cần gọi cursor.connection.commit() khi tất cả những gì tôi đang làm là chọn?