33

Chỉnh sửa lớn: Vì ban đầu tìm thấy vấn đề này tôi đã bỏ nó xuống dưới đây. Tôi nghĩ rằng đây hiện là một mô tả chính xác hơn về vấn đề này là nhẹ. Do đó, các ý kiến ​​về OP có thể không tương quan hoàn toàn.Lỗi luồng khi sử dụng `ActiveRecord with_connection do` & ActionController :: Live

Sửa phiên bản sửa đổi nhẹ posted in ray/dự án puma: https://github.com/rails/rails/issues/21209, https://github.com/puma/puma/issues/758

Sửa Bây giờ sao chép với OS X và Rainbows

Tóm tắt:Khi sử dụng Puma và chạy dài các kết nối đang chạy Tôi luôn nhận các lỗi liên quan đến các kết nối ActiveRecord qua các luồng. Điều này tự hiển thị trong thông báo nhưmessage type 0x## arrived from server while idlevà máy chủ bị khóa (bị lỗi).

Các thiết lập:

  • Ubuntu 15/OSX Yosemite
  • PostgreSQL (9.4)/MySQL (mysqld 5.6.25-0ubuntu0.15.04.1)
  • Ruby - MRI 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]/Rubinius rbx-2.5.8
  • Rails (4.2.3, 4.2.1)
  • Puma (2.12.2, 2.11)
  • pg (pg-0.18.2)/mysql2

Lưu ý, không phải tất cả các kết hợp của các phiên bản trên đã được thử nghiệm. Phiên bản được liệt kê đầu tiên là những gì tôi hiện đang thử nghiệm.

  • rails new issue-test
  • Thêm một tuyến đường get 'events' => 'streaming#events'
  • Thêm một bộ điều khiển streaming_controller.rb
  • Thiết lập thứ cơ sở dữ liệu (pool: 2, nhưng nhìn thấy với kích thước hồ bơi khác nhau)

Code:

class StreamingController < ApplicationController 

    include ActionController::Live 

    def events 
    begin 
     response.headers["Content-Type"] = "text/event-stream" 
     sse = SSE.new(response.stream) 
     sse.write({:data => 'starting'} , {:event => :version_heartbeat}) 
     ActiveRecord::Base.connection_pool.release_connection 
     while true do 
     ActiveRecord::Base.connection_pool.with_connection do |conn| 
      ActiveRecord::Base.connection.query_cache.clear 
      logger.info 'START' 
      conn.execute 'SELECT pg_sleep(3)' 
      logger.info 'FINISH' 
      sse.write({:data => 'continuing'}, {:event => :version_heartbeat}) 
      sleep 0.5 
     end 
     end 
    rescue IOError 
    rescue ClientDisconnected 
    ensure 
     logger.info 'Ensuring event stream is closed' 
     sse.close 
    end 
    render nothing: true 
    end 
end 
cấu hình

Puma:

workers 1 
threads 2, 2 
#... 
bind "tcp://0.0.0.0:9292" 

#... 
activate_control_app 

on_worker_boot do 
    require "active_record" 
    ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished 
    ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env]) 
end 
  • Run server puma -e production -C path/to/puma/config/production.rb

thử nghiệm kịch bản:

#!/bin/bash 

timeout 30 curl -vS http://0.0.0.0/events & 
timeout 5 curl -vS http://0.0.0.0/events & 
timeout 30 curl -vS http://0.0.0.0/events 

này một cách hợp lý luôn kết quả trong một khóa toàn bộ các máy chủ ứng dụng (trong PostgreSQL, xem ghi chú).Thông điệp đáng sợ đến từ libpq:

message type 0x44 arrived from server while idle 
message type 0x43 arrived from server while idle 
message type 0x5a arrived from server while idle 
message type 0x54 arrived from server while idle 

Trong 'thực tế' Tôi có khá một vài yếu tố phụ và vấn đề trình bày bản thân một cách ngẫu nhiên. Nghiên cứu của tôi chỉ ra rằng thông báo này xuất phát từ libpq và là nội dung phụ cho vấn đề giao tiếp ', có thể sử dụng kết nối trong các chủ đề khác nhau'. Cuối cùng, khi viết bài này lên, tôi đã khóa máy chủ mà không có một thông báo nào trong bất kỳ nhật ký nào.

Vì vậy, câu hỏi (s):

  1. là mô hình tôi sau không hợp pháp một cách nào đó? Những gì tôi đã sai [sed | hiểu]?
  2. 'Chuẩn' để làm việc với các kết nối cơ sở dữ liệu ở đây nên tránh những vấn đề này là gì?
  3. Bạn có thể thấy cách tái tạo lại điều này một cách đáng tin cậy không?

hoặc

  1. vấn đề cơ bản ở đây là gì và làm thế nào tôi có thể giải quyết nó?

MySQL

Nếu chạy MySQL, thông điệp là một chút khác nhau, và các ứng dụng phục hồi (mặc dù tôi không chắc chắn nếu nó là sau đó trong một số trạng thái không xác định):

F, [2015-07-30T14:12:07.078215 #15606] FATAL -- : 
ActiveRecord::StatementInvalid (Mysql2::Error: This connection is in use by: #<Thread:[email protected]/home/dev/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/live.rb:269 sleep>: SELECT `tasks`.* FROM `tasks` ORDER BY `tasks`.`id` ASC LIMIT 1): 
+1

Từ tài liệu ActiveRecord sử dụng with_connection dựa trên khối được chuyển làm đối số hoàn thành. Bạn có chắc là nó đã hoàn thành? Làm thế nào về việc xử lý kết nối với Base.connection hoặc checkout? – Grasshopper

+1

@Grasshopper - cảm ơn! Tôi lo lắng rằng điều này sẽ giữ kết nối mở cho suốt đời của yêu cầu (giờ) do đó ăn hồ bơi kết nối của tôi khá nhanh chóng? Tôi cho rằng cách mà nó có thể không hoàn thành là nếu khối sse.write vì một lý do nào đó và luồng chỉ nằm ở đó, tức là nếu kết nối đã biến mất và nó không trả lại vì một lý do nào đó? (mà nói, tôi không chắc chắn rằng giải thích đầy đủ các vấn đề dựa trên thread-vấn đề từ libpq). (sẽ thử nghiệm với một vài thứ theo hướng đó) – button

+1

Thực tế vấn đề bạn mô tả có thể xảy ra trong các tình huống mà các kết nối không được giải phóng. Bạn có thể thử xóa cuộc gọi đến sse.write khỏi khối with_connection không? – Grasshopper

Trả lời

1

Cảnh báo: đọc 'trả lời' là 'dường như tạo sự khác biệt'


Tôi không thấy vấn đề này xảy ra nếu tôi thay đổi khối điều khiển trông giống như:

begin 
    #... 
    while true do 
    t = Thread.new do #<<<<<<<<<<<<<<<<< 
     ActiveRecord::Base.connection_pool.with_connection do |conn| 
      #... 
     end 
    end 
    t.join #<<<<<<<<<<<<<<<<< 
    end 
    #... 
rescue IOError 
#... 

Nhưng tôi không biết liệu điều này đã thực sự giải quyết vấn đề hoặc chỉ làm cho nó cực kỳ khó xảy ra. Tôi cũng không hiểu tại sao điều này lại tạo nên sự khác biệt.

Đăng bài này dưới dạng giải pháp trong trường hợp giúp, nhưng vẫn tìm hiểu về vấn đề này.

+0

Vì vậy, tôi có một quá trình chạy dài quá và đây là cách tôi sử dụng nó quá. câu hỏi của tôi là, tại sao bạn bắt đầu một chuỗi cho mỗi lần lặp? nó không có ý nghĩa để bắt đầu thread và kiểm tra kết nối cho mỗi khách hàng ra khỏi vòng lặp? –

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