-
Notifications
You must be signed in to change notification settings - Fork 15
Add opt-in client/server mode to the rdx executable #869
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
Open
paracycle
wants to merge
11
commits into
uk_cli_refactor
Choose a base branch
from
uk-add-client-server-for-cli
base: uk_cli_refactor
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
ff7506e
Add opt-in client/server mode to the rdx executable
paracycle 8d54af9
Address the naming and structure review comments
paracycle 37a88c2
Test that the app id includes the protocol version
paracycle d3022d5
Harden the client and server wire protocol
paracycle 766823b
Give each direction its own read policy
paracycle 1e4cba0
Read a frame in fixed chunks
paracycle 6de387c
Make the lock decide who owns a workspace
paracycle 431b6b9
Pin the wedged server path with tests
paracycle cc3312e
Keep one bad request from taking the server down
paracycle bf69c18
Mirror the indexer when walking for changed files
paracycle b8f407e
Keep a directory that cannot be searched from erasing its files
paracycle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require "rubydex/cli/command" | ||
|
|
||
| module Rubydex | ||
| module CLI | ||
| # `rdx server <action>` — manages the resident server for the current workspace. | ||
| class Command | ||
| class Server < Command | ||
| command "server" | ||
| arguments "<action>" | ||
| summary "Manage the resident server (start, stop, restart, status)" | ||
|
|
||
| ACTIONS = ["start", "stop", "restart", "status"].freeze #: Array[String] | ||
|
|
||
| USAGE = <<~TEXT #: String | ||
| Usage: rdx server <action> [options] | ||
|
|
||
| Actions: | ||
| start Start the server for this workspace | ||
| stop Stop the running server for this workspace | ||
| restart Restart the server for this workspace | ||
| status Print the status of the server for this workspace | ||
| TEXT | ||
|
|
||
| #: -> void | ||
| def run | ||
| detach = true | ||
|
|
||
| # The options are parsed first, so that `--help` and a bad option reach the parser instead | ||
| # of being read as the action. `OptionParser#parse!` permutes, so the action can appear | ||
| # before or after an option. | ||
| parse_options!(options: true, banner: USAGE) do |parser| | ||
| parser.on("--no-detach", "Run the server in the foreground (for debugging / containers)") do | ||
| detach = false | ||
| end | ||
| end | ||
|
|
||
| action = argv.shift | ||
| abort_with_actions("unknown server action: #{action.inspect}") unless ACTIONS.include?(action) | ||
|
|
||
| require "rubydex/server" | ||
|
|
||
| unless Rubydex::Server.supported? | ||
| abort("rdx server mode is not supported on this platform (requires fork + UNIX sockets)") | ||
| end | ||
|
|
||
| exit(dispatch_action(action, detach)) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| #: (String action, bool detach) -> Integer | ||
| def dispatch_action(action, detach) | ||
| state = Rubydex::Server::State.new(workspace_path: Dir.pwd) | ||
|
|
||
| case action | ||
| when "start" then Rubydex::Server::Commands.start(state, detach: detach) | ||
| when "stop" then Rubydex::Server::Commands.stop(state) | ||
| when "restart" then Rubydex::Server::Commands.restart(state, detach: detach) | ||
| else Rubydex::Server::Commands.status(state) | ||
| end | ||
| end | ||
|
|
||
| # Reports `message` with the action list rather than the top-level command list, because the | ||
| # error is about an action of this command. | ||
| #: (String message) -> void | ||
| def abort_with_actions(message) | ||
| warn(message) | ||
| warn("") | ||
| warn(USAGE) | ||
| exit(1) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Rubydex | ||
| # Reports how long a step took. The CLI and the server both print the same progress lines, so the | ||
| # measurement lives here rather than in either of them. | ||
| module Progress | ||
| class << self | ||
| # Runs the block and reports its duration to `io`. A `nil` `io` runs the block and reports | ||
| # nothing, which is what the server does when it has no log. | ||
| #: (IO? io, String message) { -> void } -> void | ||
| def with_timer(io, message) | ||
| unless io | ||
| yield | ||
| return | ||
| end | ||
|
|
||
| io.print(message) | ||
| start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) | ||
| yield | ||
| duration = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start | ||
| io.puts(" finished in #{duration.round(2)}ms") | ||
| end | ||
| end | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require "rubydex/progress" | ||
| require "rubydex/version" | ||
|
|
||
| module Rubydex | ||
| # Client/server mode for the `rdx` executable. | ||
| # | ||
| # The expensive indexing + resolution work is performed once by a resident server process that | ||
| # keeps the built `Rubydex::Graph` in memory. Subsequent commands (currently `--query`) run against | ||
| # the already-built graph over a UNIX domain socket, making follow-up queries effectively instant. | ||
| # | ||
| module Server | ||
| # Wire protocol version. Bump on any incompatible change to the request/response shape. | ||
| PROTOCOL = 1 | ||
|
|
||
| class Error < StandardError; end | ||
|
|
||
| class << self | ||
| # Whether server mode can run on the current platform. Requires `fork` + UNIX domain sockets. | ||
| #: -> bool | ||
| def supported? | ||
| Process.respond_to?(:fork) && defined?(::UNIXSocket) && !Gem.win_platform? | ||
| end | ||
|
|
||
| # Whether the user has explicitly disabled the server via the environment. | ||
| #: -> bool | ||
| def disabled? | ||
| ENV.key?("DISABLE_RDX_SERVER") | ||
| end | ||
|
Comment on lines
+26
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this? |
||
|
|
||
| # Builds a fully indexed + resolved graph for the workspace, and returns it with the errors the | ||
| # indexer reported. `progress_io`, when given, receives human-readable progress messages. | ||
| # | ||
| # A caller that discards the errors records a file the indexer never read as successfully | ||
| # indexed, which is why they come back rather than vanishing here. | ||
| #: (workspace_path: String, ?progress_io: IO?) -> [Rubydex::Graph, Array[String]] | ||
| def build_graph(workspace_path:, progress_io: nil) | ||
| # `configure_for_workspace` builds the graph rooted at `workspace_path` and applies that | ||
| # workspace's `.rubydex` config (exclusions etc.) before indexing, matching the inline CLI | ||
| # path. A missing default config is ignored. | ||
| graph = Rubydex::Graph.configure_for_workspace(workspace_path) | ||
|
|
||
| # `workspace_paths` lists every root to index, and it names gem directories that this install | ||
| # may not have. Each absent root costs one error, and those phantom errors would drown the | ||
| # ones that concern real files, so they never reach the indexer. | ||
| roots = graph.workspace_paths.select { |path| File.exist?(path) } | ||
|
|
||
| errors = [] #: Array[String] | ||
| Progress.with_timer(progress_io, "Indexing workspace...") { errors = graph.index_all(roots) } | ||
| Progress.with_timer(progress_io, "Resolving graph...") { graph.resolve } | ||
| [graph, errors] | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| require "rubydex/server/state" | ||
| require "rubydex/server/request" | ||
| require "rubydex/server/core" | ||
| require "rubydex/server/client" | ||
| require "rubydex/server/commands" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do we ever enforce this?