Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix #11984] Add configurable server idle timeout #11985

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/new_add_configurable_server_idle_timeout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#11984](https://github.com/rubocop/rubocop/issues/11984): Add configurable server idle timeout via the `RUBOCOP_SERVER_IDLE_TIMEOUT` environment variable. ([@Palladinium][])
8 changes: 6 additions & 2 deletions docs/modules/ROOT/pages/usage/server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,12 @@ environment variables.
* `$RUBOCOP_SERVER_HOST`
* `$RUBOCOP_SERVER_PORT`

The following is an example:
You can configure the server process to automatically shut down after a period of not receiving connections. The option takes a number in seconds.

* `$RUBOCOP_SERVER_IDLE_TIMEOUT`

The following is an example that listens on port 98989 and shuts down after one minute without receiving connections:

```console
$ RUBOCOP_SERVER_PORT=98989 rubocop --start-server
$ RUBOCOP_SERVER_PORT=98989 RUBOCOP_SERVER_IDLE_TIMEOUT=60 rubocop --start-server
```
6 changes: 5 additions & 1 deletion lib/rubocop/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,11 @@ module OptionsHelp
'Default is false.',
'You can specify the server host and port with the',
'$RUBOCOP_SERVER_HOST and the $RUBOCOP_SERVER_PORT',
'environment variables.'],
'environment variables.',
'You can set a timeout in seconds with the',
'$RUBOCOP_SERVER_IDLE_TIMEOUT environment variable,',
'after which the server will shut down if no connections',
'are made.'],
restart_server: 'Restart server process.',
start_server: 'Start server process.',
stop_server: 'Stop server process.',
Expand Down
5 changes: 4 additions & 1 deletion lib/rubocop/server/client_command/start.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def initialize(detach: true)
super()
end

# rubocop:disable Metrics/MethodLength
def run
if Server.running?
warn "RuboCop server (#{Cache.pid_path.read}) is already running."
Expand All @@ -38,10 +39,12 @@ def run

host = ENV.fetch('RUBOCOP_SERVER_HOST', '127.0.0.1')
port = ENV.fetch('RUBOCOP_SERVER_PORT', 0)
idle_timeout = ENV.fetch('RUBOCOP_SERVER_IDLE_TIMEOUT', nil)&.to_i

Server::Core.new.start(host, port, detach: @detach)
Server::Core.new.start(host, port, detach: @detach, idle_timeout: idle_timeout)
end
end
# rubocop:enable Metrics/MethodLength
end
end
end
Expand Down
33 changes: 25 additions & 8 deletions lib/rubocop/server/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def token
self.class.token
end

def start(host, port, detach: true)
def start(host, port, detach: true, idle_timeout: nil)
@idle_timeout = idle_timeout
$PROGRAM_NAME = "rubocop --server #{Cache.project_dir}"

require_relative '../../rubocop'
Expand Down Expand Up @@ -58,7 +59,20 @@ def write_port_and_token_files

def process_input
Cache.write_pid_file do
read_socket(@server.accept) until @server.closed?
until @server.closed?
begin
socket = @server.accept_nonblock
rescue IO::WaitReadable, Errno::EINTR
if @server.wait_readable(@idle_timeout).nil?
output_stream&.puts 'RuboCop server terminating due to idle timeout.'
break
end

retry
end

read_socket(socket)
end
end
end

Expand All @@ -71,15 +85,18 @@ def server_mode?
true
end

def start_server(host, port)
@server = TCPServer.open(host, port)

def output_stream
# JSON format does not expected output message when IDE integration with server mode.
# See: https://github.com/rubocop/rubocop/issues/11164
return if use_json_format?
return nil if use_json_format?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change intentional?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was just force of habit, I forgot the two are equivalent. I'll revert it since the older line is more concise.


ARGV.include?('--stderr') ? $stderr : $stdout
end

def start_server(host, port)
@server = TCPServer.open(host, port)

output_stream = ARGV.include?('--stderr') ? $stderr : $stdout
output_stream.puts "RuboCop server starting on #{@server.addr[3]}:#{@server.addr[1]}."
output_stream&.puts "RuboCop server starting on #{@server.addr[3]}:#{@server.addr[1]}."
end

def read_socket(socket)
Expand Down
4 changes: 4 additions & 0 deletions spec/rubocop/options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ def abs(path)
You can specify the server host and port with the
$RUBOCOP_SERVER_HOST and the $RUBOCOP_SERVER_PORT
environment variables.
You can set a timeout in seconds with the
$RUBOCOP_SERVER_IDLE_TIMEOUT environment variable,
after which the server will shut down if no connections
are made.
--restart-server Restart server process.
--start-server Start server process.
--stop-server Stop server process.
Expand Down