主要编辑:由于最初发现此问题,我已将其缩减至下方。我认为现在这是一个略微更精确的问题描述。因此,对OP的评论可能无法完全相关。
在rails / puma项目中发布的编辑轻微修改后的版本:https://github.com/rails/rails/issues/21209,https://github.com/puma/puma/issues/758
编辑现在使用OS X和Rainbows重现
摘要: 当使用Puma并运行长时间运行的连接时,我一直收到与跨越线程的ActiveRecord连接相关的错误。这表现在消息中,如 message type 0x## arrived from server while idle
和锁定(崩溃)的服务器。
设置:
mysqld 5.6.25-0ubuntu0.15.04.1
)2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
/ Rubinius rbx-2.5.8
4.2.3
,4.2.1
)2.12.2
,2.11
)pg-0.18.2
)/ mysql2 请注意,并非所有上述版本的组合都已尝试过。首先列出的版本是我目前正在测试的版本。
rails new issue-test
get 'events' => 'streaming#events'
streaming_controller.rb
pool: 2
,但看到不同的池大小)代码:
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
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
puma -e production -C path/to/puma/config/production.rb
测试脚本:
#!/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
这合理地始终导致应用程序服务器的完全锁定(在PostgreSQL中,请参阅注释)。可怕的消息来自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
在现实世界中&#39;我有很多额外的元素,这个问题随机出现。我的研究表明此消息来自libpq
,并且是&#39;通信问题的潜台词,可能使用不同线程中的连接&#39; 。最后,在编写本文时,我将服务器锁定在任何日志中都没有单个消息。
所以,问题是:
或
的MySQL
如果运行MySQL,消息会有所不同,应用程序会恢复(尽管我不确定它是否处于某种未定义状态):
F, [2015-07-30T14:12:07.078215 #15606] FATAL -- :
ActiveRecord::StatementInvalid (Mysql2::Error: This connection is in use by: #<Thread:0x007f563b2faa88@/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):
答案 0 :(得分:1)
警告:将“回答”视为“似乎有所作为”
如果我将控制器块更改为:
,我不会发现问题//#define OLDSCHOOL_INVOKE
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AsyncTests
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void LongTermOp()
{
int delay;
int thisId;
lock (mtx1)
{
delay = rnd.Next(2000, 10000);
thisId = firstCount++;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label1Gen.Text = $"Generating first run delay #{thisId} of {delay} ms"
#if OLDSCHOOL_INVOKE
))
#endif
;
++firstPending;
}
await Task.Delay(delay);
lock (mtx1)
{
--firstPending;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label1Gen.Text = $"First run #{thisId} completed, {firstPending} pending..."
#if OLDSCHOOL_INVOKE
))
#endif
;
}
}
private async Task LongTermOpAsync()
{
await Task.Run((Action)LongTermOp);
}
private readonly Random rnd = new Random();
private readonly object mtx1 = new object();
private readonly object mtx2 = new object();
private int firstCount;
private int firstPending;
private int secondCount;
private int secondPending;
private async void buttonRound1_Click(object sender, EventArgs e)
{
await LongTermOpAsync();
}
private async void buttonRound2_Click(object sender, EventArgs e)
{
await Task.Run(async () =>
{
int delay;
int thisId;
lock (mtx2)
{
delay = rnd.Next(2000, 10000);
thisId = secondCount++;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label2Gen.Text = $"Generating second run delay #{thisId} of {delay} ms"
#if OLDSCHOOL_INVOKE
))
#endif
;
++secondPending;
}
await Task.Delay(delay);
lock (mtx2)
{
--secondPending;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label2Gen.Text = $"Second run #{thisId} completed, {secondPending} pending..."
#if OLDSCHOOL_INVOKE
))
#endif
;
}
});
}
private void buttonRound12_Click(object sender, EventArgs e)
{
buttonRound1_Click(sender, e);
buttonRound2_Click(sender, e);
}
private bool isRunning = false;
private async void buttonCycle_Click(object sender, EventArgs e)
{
isRunning = !isRunning;
await Task.Run(() =>
{
while (isRunning)
{
buttonRound12_Click(sender, e);
Application.DoEvents();
}
});
}
}
}
但我不知道这是否真的解决了这个问题,或者只是让它变得极不可能。我也无法理解为什么会有所作为。
将此作为解决方案发布,以防万一,但仍在挖掘问题。