2011-10-04 33 views
5

Tôi đang sử dụng Ruby 1.9.2.Sử dụng thời gian chờ ruby ​​trong chuỗi tạo một cuộc gọi cơ sở dữ liệu

Tôi có một chuỗi chạy đang thực hiện cuộc gọi định kỳ tới cơ sở dữ liệu. Các cuộc gọi có thể khá dài, và đôi khi (vì nhiều lý do khác nhau) kết nối DB biến mất. Nếu nó biến mất, sợi chỉ lặng lẽ treo ở đó mãi mãi.

Vì vậy, tôi muốn kết thúc tất cả trong một thời gian chờ để xử lý việc này. Vấn đề là, vào lần thứ hai thông qua khi một thời gian chờ nên được gọi (luôn luôn thứ hai), nó vẫn chỉ đơn giản là treo cứng. Thời gian chờ không bao giờ có hiệu lực. Tôi biết vấn đề này tồn tại trong 1.8, nhưng tôi đã dẫn đến tin timeout.rb làm việc trong 1.9.

t = Thread.new do 
    while true do 
    sleep SLEEPTIME 
    begin 
     Timeout::timeout(TIMEOUTTIME) do 
     puts "About to do DB stuff, it will hang here on the second timeout" 
     db.do_db_stuff() 
     process_db_stuff() 
     end 
    rescue Timeout::Error 
     puts "Timed out" 
     #handle stuff here 
    end 
    end 
end 

Bất kỳ ý tưởng nào tại sao điều này đang xảy ra và tôi có thể làm gì?

+0

Vì vậy, nó không ném Timeout :: Lỗi? Là nó ném bất kỳ lỗi nào cả? –

+0

Chính xác, không có gì xảy ra, nó chỉ đóng băng ngay tại vị trí của nó. Ít nhất, đó là những gì tôi đã xác định từ đặt quá nhiều 'đặt' ở khắp mọi nơi để xác định nơi/nếu thực hiện được chọn sao lưu. –

+0

Tại sao điều đó lại làm việc ném? Toàn bộ thời gian chờ sẽ là nó gây ra lỗi nếu khối nó được cho chạy quá lâu. Tôi đã thử datamapper và mysql2 trực tiếp, hy vọng hoặc sẽ làm một kết nối lại tự động và giải quyết vấn đề theo cách đó, nhưng không có may mắn. Nếu các chi tiết cụ thể giúp đỡ, hãy nói đó là kết nối mysql2 mà trên đó tôi thực hiện một '.query (...)'. Không có gì được bao giờ ném từ đó, anyways, và một điều khoản 'rescue' chung bên ngoài nó sẽ bắt nó trong bất kỳ sự kiện nào. –

Trả lời

5

Một khả năng là chuỗi của bạn không bị treo, nó thực sự là chết. Đây là những gì bạn nên làm để tìm ra những gì đang xảy ra. Thêm này trước khi tạo thread công nhân của bạn:

Thread.abort_on_exception = true 

Khi một ngoại lệ xảy ra bên trong chủ đề của bạn mà không bao giờ bị bắt, toàn bộ quá trình bạn bị chấm dứt, và bạn có thể xem những ngoại lệ được nâng lên. Nếu không (và đây là mặc định), chủ đề của bạn bị giết.

Nếu đây quay ra không là vấn đề, hãy đọc tiếp ...

thi hành timeouts Ruby là khá ngây thơ. Nó thiết lập một chuỗi riêng biệt để ngủ cho n giây, sau đó mù quáng làm tăng ngoại lệ Thời gian chờ bên trong chuỗi ban đầu.

Bây giờ, mã gốc thực sự có thể ở giữa khối rescue hoặc ensure. Việc đưa ra một ngoại lệ trong một khối như vậy sẽ âm thầm hủy bỏ bất kỳ loại mã dọn dẹp nào. Điều này có thể để lại mã mà lần trong trạng thái không đúng. Nó khá khó để nói nếu đây là vấn đề của bạn chính xác, nhưng nhìn thấy cách xử lý cơ sở dữ liệu có thể làm một chút công bằng của khóa và xử lý ngoại lệ, nó có thể rất có thể. Here's an article that explains the issue in more depth.

Có cách nào bạn có thể sử dụng tính năng xử lý hết thời gian chờ tích hợp sẵn của thư viện cơ sở dữ liệu không? Nó có thể được thực hiện ở mức thấp hơn, không sử dụng thời gian chờ của Ruby.

Cách thay thế đơn giản là lên lịch cuộc gọi cơ sở dữ liệu trong một quy trình riêng biệt. Bạn có thể ngã ba quá trình chính mỗi khi bạn làm việc nâng cơ sở dữ liệu nặng nề. Hoặc bạn có thể thiết lập một cronjob đơn giản để thực thi một kịch bản thực thi nó. Điều này sẽ hơi khó khăn hơn nếu bạn cần giao tiếp với chủ đề chính của bạn. Vui lòng để lại một số chi tiết khác nếu bạn muốn bất kỳ lời khuyên nào về tùy chọn nào có thể phù hợp với nhu cầu của bạn.


Dựa trên nhận xét của bạn, chuỗi đang sắp chết. Đây có thể là lỗi trong các thư viện hoặc mã ứng dụng mà bạn có thể hoặc không thể khắc phục được.Nếu bạn muốn bẫy bất kỳ lỗi tùy ý được tạo ra bởi mã xử lý cơ sở dữ liệu và sau đó thử lại, bạn có thể thử một cái gì đó như sau:

t = Thread.new do 
    loop do 
    sleep INTERVAL 
    begin 
     # Execute database queries and process data 
    rescue StandardError 
     # Log error or recover from error situation before retrying 
    end 
    end 
end 

Bạn cũng có thể sử dụng từ khóa retry trong khối rescue để thử lại ngay lập tức, nhưng bạn có thể nên giữ một bộ đếm để đảm bảo rằng bạn không vô tình thử lại vô thời hạn khi lỗi không thể khôi phục tiếp tục xảy ra.

+0

Nó xuất hiện nó thực sự chết, mặc dù bây giờ tôi không hiểu lỗi tôi nhận được: 'giải cứu trong khối trong ': uninitialized liên tục Object :: Error (NameError)' –

+0

Dường như nó là thực sự chết, mặc dù bây giờ tôi không hiểu lỗi tôi nhận được: 'giải cứu trong khối trong <đầu (yêu cầu)> ': uninitialized đối tượng liên tục :: Lỗi (NameError)'. Bất kể điều đó, tuy nhiên, làm thế nào tôi có thể xử lý này? Các dữ liệu tôi nhận được từ db được coallated và coalesced với các công cụ khác và sau đó dịch vụ gọi nó, vì vậy tôi muốn có thể đẻ trứng một thread giống hệt nhau nếu/khi thread này chết. Vì nó luôn luôn xảy ra _after_ timeout đầu tiên, tôi cũng có thể chủ động giết thread trong trình xử lý thời gian chờ, một lần nữa, nếu tôi có thể sao chép các chủ đề ở nơi khác. –

+0

Điều cần biết! Nó sẽ là tốt nhất để tìm ra nguyên nhân của ngoại lệ này và sửa chữa nó, nhưng nó có thể là đủ để nắm bắt những trường hợp ngoại lệ và chỉ cần thử lại. Câu trả lời đã được cập nhật để giải thích cách thức. – molf

Các vấn đề liên quan