Ruby TCPServer performance issue









up vote
0
down vote

favorite












I am encountering an interesting issue with Ruby TCPServer, where once a client connects, it continually uses more and more CPU processing power until it hits 100% and then the entire system starts to bog down and can't process incoming data.



The processing class that is having an issue is designed to be a TCP Client that receives data from an embedded system, processes it, then returns the processed data to be further used (either by other similar data processors, or output to a user).



In this particular case, there is an external piece of code that would like this processed data, but cannot access it from the main parent code (the thing that the original process class is returning it's data to). This external piece may or may not be connected at any point while it is running.



To solve this, I set up a Thread with a TCPServer, and the processing class continually adds to a queue, and the Thread pulls from the queue and sends it to the client.



It works great, except for the performance issues. I am curious if I have something funky going on in my code, or if it's just the nature of this methodology and it will never be performant enough to work.



Thanks in advance for any insight/suggestions with this problem!



Here is my code/setup, with some test helpers:



process_data.rb



require 'socket'

class ProcessData

def initialize
super

@queue = Queue.new
@client_active = false

Thread.new do
# Waiting for connection
@server = TCPServer.open('localhost', 5000)

loop do

Thread.start(@server.accept) do |client|
puts 'Client connected'

# Connection established
@client_active = true

begin
# Continually attempt to send data to client
loop do

unless @queue.empty?
# If data exists, send it to client
begin
until @queue.empty?
client.puts(@queue.pop)
end
rescue Errno::EPIPE => error
# Client disconnected
client.close
end
end
sleep(1)
end

rescue IOError => error
# Client disconnected
@client_active = false
end
end # Thread.start(@server.accept)
end # loop do
end # Thread.new do

end



def read(data)
# Data comes in from embedded system on this method

# Do some processing
processed_data = data.to_i + 5678

# Ready to send data to external client
if @client_active
@queue << processed_data
end

return processed_data
end

end


test_embedded_system.rb (source of the original data)



require 'socket'

@data = '1234'*100000 # Simulate lots of data coming ing

embedded_system = TCPServer.open('localhost', 5555)

client_connection = embedded_system.accept
loop do
client_connection.puts(@data)
sleep(0.1)
end


parent.rb (this is what will create/call the ProcessData class)



require_relative 'process_data'

processor = ProcessData.new
loop do
begin
s = TCPSocket.new('localhost', 5555)
while data = s.gets
processor.read(data)
end
rescue => e
sleep(1)
end
end


random_client.rb (wants data from ProcessData)



require 'socket'

loop do
begin
s = TCPSocket.new('localhost', 5000)
while processed_data = s.gets
puts processed_data
end
rescue => e
sleep(1)
end
end


To run the test in linux, open 3 terminal windows:



Window 1: ./test_embedded_system.rb



Window 2: ./parent.rb



CPU usage is stable



Window 3: ./random_client.rb



CPU usage continually grows










share|improve this question























  • is what you provided going to reproduce the CPU issue? Can you share your version of ruby
    – Anthony
    Nov 8 at 19:49










  • Ruby version: ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux] Let me see if I can put together a good way to test it, I believe this is the correct code to reproduce the issue, but it's definitely not a trivial setup
    – Tennesseej
    Nov 8 at 20:04










  • @Anthony I added a test to the question, let me know if that helps!
    – Tennesseej
    Nov 8 at 21:19














up vote
0
down vote

favorite












I am encountering an interesting issue with Ruby TCPServer, where once a client connects, it continually uses more and more CPU processing power until it hits 100% and then the entire system starts to bog down and can't process incoming data.



The processing class that is having an issue is designed to be a TCP Client that receives data from an embedded system, processes it, then returns the processed data to be further used (either by other similar data processors, or output to a user).



In this particular case, there is an external piece of code that would like this processed data, but cannot access it from the main parent code (the thing that the original process class is returning it's data to). This external piece may or may not be connected at any point while it is running.



To solve this, I set up a Thread with a TCPServer, and the processing class continually adds to a queue, and the Thread pulls from the queue and sends it to the client.



It works great, except for the performance issues. I am curious if I have something funky going on in my code, or if it's just the nature of this methodology and it will never be performant enough to work.



Thanks in advance for any insight/suggestions with this problem!



Here is my code/setup, with some test helpers:



process_data.rb



require 'socket'

class ProcessData

def initialize
super

@queue = Queue.new
@client_active = false

Thread.new do
# Waiting for connection
@server = TCPServer.open('localhost', 5000)

loop do

Thread.start(@server.accept) do |client|
puts 'Client connected'

# Connection established
@client_active = true

begin
# Continually attempt to send data to client
loop do

unless @queue.empty?
# If data exists, send it to client
begin
until @queue.empty?
client.puts(@queue.pop)
end
rescue Errno::EPIPE => error
# Client disconnected
client.close
end
end
sleep(1)
end

rescue IOError => error
# Client disconnected
@client_active = false
end
end # Thread.start(@server.accept)
end # loop do
end # Thread.new do

end



def read(data)
# Data comes in from embedded system on this method

# Do some processing
processed_data = data.to_i + 5678

# Ready to send data to external client
if @client_active
@queue << processed_data
end

return processed_data
end

end


test_embedded_system.rb (source of the original data)



require 'socket'

@data = '1234'*100000 # Simulate lots of data coming ing

embedded_system = TCPServer.open('localhost', 5555)

client_connection = embedded_system.accept
loop do
client_connection.puts(@data)
sleep(0.1)
end


parent.rb (this is what will create/call the ProcessData class)



require_relative 'process_data'

processor = ProcessData.new
loop do
begin
s = TCPSocket.new('localhost', 5555)
while data = s.gets
processor.read(data)
end
rescue => e
sleep(1)
end
end


random_client.rb (wants data from ProcessData)



require 'socket'

loop do
begin
s = TCPSocket.new('localhost', 5000)
while processed_data = s.gets
puts processed_data
end
rescue => e
sleep(1)
end
end


To run the test in linux, open 3 terminal windows:



Window 1: ./test_embedded_system.rb



Window 2: ./parent.rb



CPU usage is stable



Window 3: ./random_client.rb



CPU usage continually grows










share|improve this question























  • is what you provided going to reproduce the CPU issue? Can you share your version of ruby
    – Anthony
    Nov 8 at 19:49










  • Ruby version: ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux] Let me see if I can put together a good way to test it, I believe this is the correct code to reproduce the issue, but it's definitely not a trivial setup
    – Tennesseej
    Nov 8 at 20:04










  • @Anthony I added a test to the question, let me know if that helps!
    – Tennesseej
    Nov 8 at 21:19












up vote
0
down vote

favorite









up vote
0
down vote

favorite











I am encountering an interesting issue with Ruby TCPServer, where once a client connects, it continually uses more and more CPU processing power until it hits 100% and then the entire system starts to bog down and can't process incoming data.



The processing class that is having an issue is designed to be a TCP Client that receives data from an embedded system, processes it, then returns the processed data to be further used (either by other similar data processors, or output to a user).



In this particular case, there is an external piece of code that would like this processed data, but cannot access it from the main parent code (the thing that the original process class is returning it's data to). This external piece may or may not be connected at any point while it is running.



To solve this, I set up a Thread with a TCPServer, and the processing class continually adds to a queue, and the Thread pulls from the queue and sends it to the client.



It works great, except for the performance issues. I am curious if I have something funky going on in my code, or if it's just the nature of this methodology and it will never be performant enough to work.



Thanks in advance for any insight/suggestions with this problem!



Here is my code/setup, with some test helpers:



process_data.rb



require 'socket'

class ProcessData

def initialize
super

@queue = Queue.new
@client_active = false

Thread.new do
# Waiting for connection
@server = TCPServer.open('localhost', 5000)

loop do

Thread.start(@server.accept) do |client|
puts 'Client connected'

# Connection established
@client_active = true

begin
# Continually attempt to send data to client
loop do

unless @queue.empty?
# If data exists, send it to client
begin
until @queue.empty?
client.puts(@queue.pop)
end
rescue Errno::EPIPE => error
# Client disconnected
client.close
end
end
sleep(1)
end

rescue IOError => error
# Client disconnected
@client_active = false
end
end # Thread.start(@server.accept)
end # loop do
end # Thread.new do

end



def read(data)
# Data comes in from embedded system on this method

# Do some processing
processed_data = data.to_i + 5678

# Ready to send data to external client
if @client_active
@queue << processed_data
end

return processed_data
end

end


test_embedded_system.rb (source of the original data)



require 'socket'

@data = '1234'*100000 # Simulate lots of data coming ing

embedded_system = TCPServer.open('localhost', 5555)

client_connection = embedded_system.accept
loop do
client_connection.puts(@data)
sleep(0.1)
end


parent.rb (this is what will create/call the ProcessData class)



require_relative 'process_data'

processor = ProcessData.new
loop do
begin
s = TCPSocket.new('localhost', 5555)
while data = s.gets
processor.read(data)
end
rescue => e
sleep(1)
end
end


random_client.rb (wants data from ProcessData)



require 'socket'

loop do
begin
s = TCPSocket.new('localhost', 5000)
while processed_data = s.gets
puts processed_data
end
rescue => e
sleep(1)
end
end


To run the test in linux, open 3 terminal windows:



Window 1: ./test_embedded_system.rb



Window 2: ./parent.rb



CPU usage is stable



Window 3: ./random_client.rb



CPU usage continually grows










share|improve this question















I am encountering an interesting issue with Ruby TCPServer, where once a client connects, it continually uses more and more CPU processing power until it hits 100% and then the entire system starts to bog down and can't process incoming data.



The processing class that is having an issue is designed to be a TCP Client that receives data from an embedded system, processes it, then returns the processed data to be further used (either by other similar data processors, or output to a user).



In this particular case, there is an external piece of code that would like this processed data, but cannot access it from the main parent code (the thing that the original process class is returning it's data to). This external piece may or may not be connected at any point while it is running.



To solve this, I set up a Thread with a TCPServer, and the processing class continually adds to a queue, and the Thread pulls from the queue and sends it to the client.



It works great, except for the performance issues. I am curious if I have something funky going on in my code, or if it's just the nature of this methodology and it will never be performant enough to work.



Thanks in advance for any insight/suggestions with this problem!



Here is my code/setup, with some test helpers:



process_data.rb



require 'socket'

class ProcessData

def initialize
super

@queue = Queue.new
@client_active = false

Thread.new do
# Waiting for connection
@server = TCPServer.open('localhost', 5000)

loop do

Thread.start(@server.accept) do |client|
puts 'Client connected'

# Connection established
@client_active = true

begin
# Continually attempt to send data to client
loop do

unless @queue.empty?
# If data exists, send it to client
begin
until @queue.empty?
client.puts(@queue.pop)
end
rescue Errno::EPIPE => error
# Client disconnected
client.close
end
end
sleep(1)
end

rescue IOError => error
# Client disconnected
@client_active = false
end
end # Thread.start(@server.accept)
end # loop do
end # Thread.new do

end



def read(data)
# Data comes in from embedded system on this method

# Do some processing
processed_data = data.to_i + 5678

# Ready to send data to external client
if @client_active
@queue << processed_data
end

return processed_data
end

end


test_embedded_system.rb (source of the original data)



require 'socket'

@data = '1234'*100000 # Simulate lots of data coming ing

embedded_system = TCPServer.open('localhost', 5555)

client_connection = embedded_system.accept
loop do
client_connection.puts(@data)
sleep(0.1)
end


parent.rb (this is what will create/call the ProcessData class)



require_relative 'process_data'

processor = ProcessData.new
loop do
begin
s = TCPSocket.new('localhost', 5555)
while data = s.gets
processor.read(data)
end
rescue => e
sleep(1)
end
end


random_client.rb (wants data from ProcessData)



require 'socket'

loop do
begin
s = TCPSocket.new('localhost', 5000)
while processed_data = s.gets
puts processed_data
end
rescue => e
sleep(1)
end
end


To run the test in linux, open 3 terminal windows:



Window 1: ./test_embedded_system.rb



Window 2: ./parent.rb



CPU usage is stable



Window 3: ./random_client.rb



CPU usage continually grows







ruby multithreading tcp tcpclient tcpserver






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 8 at 21:19

























asked Nov 8 at 19:15









Tennesseej

119129




119129











  • is what you provided going to reproduce the CPU issue? Can you share your version of ruby
    – Anthony
    Nov 8 at 19:49










  • Ruby version: ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux] Let me see if I can put together a good way to test it, I believe this is the correct code to reproduce the issue, but it's definitely not a trivial setup
    – Tennesseej
    Nov 8 at 20:04










  • @Anthony I added a test to the question, let me know if that helps!
    – Tennesseej
    Nov 8 at 21:19
















  • is what you provided going to reproduce the CPU issue? Can you share your version of ruby
    – Anthony
    Nov 8 at 19:49










  • Ruby version: ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux] Let me see if I can put together a good way to test it, I believe this is the correct code to reproduce the issue, but it's definitely not a trivial setup
    – Tennesseej
    Nov 8 at 20:04










  • @Anthony I added a test to the question, let me know if that helps!
    – Tennesseej
    Nov 8 at 21:19















is what you provided going to reproduce the CPU issue? Can you share your version of ruby
– Anthony
Nov 8 at 19:49




is what you provided going to reproduce the CPU issue? Can you share your version of ruby
– Anthony
Nov 8 at 19:49












Ruby version: ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux] Let me see if I can put together a good way to test it, I believe this is the correct code to reproduce the issue, but it's definitely not a trivial setup
– Tennesseej
Nov 8 at 20:04




Ruby version: ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux] Let me see if I can put together a good way to test it, I believe this is the correct code to reproduce the issue, but it's definitely not a trivial setup
– Tennesseej
Nov 8 at 20:04












@Anthony I added a test to the question, let me know if that helps!
– Tennesseej
Nov 8 at 21:19




@Anthony I added a test to the question, let me know if that helps!
– Tennesseej
Nov 8 at 21:19

















active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53214671%2fruby-tcpserver-performance-issue%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown






























active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53214671%2fruby-tcpserver-performance-issue%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)