blob: d65a021097a6597353d253be190811b98dcc304b [file] [log] [blame] [view]
# Running gopls as a daemon
**Note: this feature is new. If you encounter bugs, please [file an
issue](troubleshooting.md#file-an-issue).**
If you just want to try this out, skip ahead to the [quickstart](#quickstart).
## Background: gopls execution modes
Gopls was originally implemented as an LSP sidecar: a process started by
editors or editor plugins, and communicated with using jsonrpc 2.0 over
stdin/stdout. By executing as a stateful process, gopls can maintain a
significant amount of cache and can eagerly perform analysis on the source code
being edited.
This execution mode does not work as well when there are many separate editor
processes or when editor processes are short-lived, as is often the case for
users of non-IDE editors such as Vim or Emacs. Having many processes means
having many caches, consuming a significant amount of system resources. Using
short-lived sessions means paying a start-up cost each time a session is
created.
To support these types of workflows, a new mode of gopls execution is supported
wherein a single, persistent, shared gopls "daemon" process is responsible for
managing all gopls sessions. In this mode, editors still start a gopls sidecar,
but this sidecar merely acts as a thin "forwarder", responsible for forwarding
the LSP to the shared gopls instance and recording metrics, logs, and rpc
traces.
## Quickstart
To use a shared gopls instance you must either manage the daemon process
yourself, or let the gopls forwarder processes start the shared daemon as
needed.
### Running with `-remote=auto`
Automatic management of the daemon is easiest, and can be done by passing the
flag `-remote=auto` to the gopls process started by your editor. This will
cause this process to auto-start the gopls daemon if needed, connect to it, and
forward the LSP. For example, here is a reasonable gopls invocation, that sets
some additional flags for easier [debugging](#debugging):
```
$ gopls -remote=auto -logfile=auto -debug=:0 -remote.debug=:0 -rpc.trace
```
Note that the shared gopls process will automatically shut down after one
minute with no connected clients.
### Managing the daemon manually
To manage the gopls daemon process via external means rather than having the
forwarders manage it, you must start a gopls daemon process with the
`-listen=<addr>` flag, and then pass `-remote=<addr>` to the gopls processes
started by your editor.
For example, to host the daemon on the TCP port `37374`, do:
```
$ gopls -listen=:37374 -logfile=auto -debug=:0
```
And then from the editor, run
```
$ gopls -remote=:37374 -logfile=auto -debug=:0 -rpc.trace
```
If you are on a POSIX system, you can also use unix domain sockets by prefixing
the flag values with `unix;`. For example:
```
$ gopls -listen="unix;/tmp/gopls-daemon-socket" -logfile=auto -debug=:0
```
And connect via:
```
$ gopls -remote="unix;/tmp/gopls-daemon-socket" -logfile=auto -debug=:0 -rpc.trace
```
(Note that these flag values MUST be enclosed in quotes, because ';' is a
special shell character. For this reason, this syntax is subject to change in
the future.)
## Debugging
Debugging a shared gopls session is more complicated than a singleton session,
because there are now two gopls processes involved with handling the LSP. Here
are some tips:
### Finding logfiles and debug addresses
When running in daemon mode, you can use the `gopls inspect sessions` command
to find the logfile and debug port for your gopls daemon instance (as well as
for all its connected clients). By default, this inspects the default daemon
(i.e. `-remote=auto`). To inspect a different daemon, use the `-remote` flag
explicitly: `gopls -remote=localhost:12345 inspect sessions`.
This works whether or not you have enabled `-remote.debug`.
### Traversing debug pages
When `-debug=:0` is passed to gopls, it runs a webserver that serves stateful
debug pages (see [troubleshooting.md](troubleshooting.md)). You can find the
actual port hosting these pages by either using the `gopls inspect sessions`
command, or by checking the start of the logfile -- it will be one of the first
log messages. For example, if using `-logfile=auto`, find the debug address by
checking `head /tmp/gopls-<pid>.log`.
By default, the gopls daemon is not started with `-debug`. To enable it, set
the `-remote.debug` flag on the forwarder instance, so that it invokes gopls
with `-debug` when starting the daemon.
The debug pages of the forwarder process will have a link to the debug pages of
the daemon server process. Correspondingly, the debug pages of the daemon
process will have a link to each of its clients.
This can help you find metrics, traces, and log files for all of the various
servers and clients.
### Using logfiles
The gopls daemon is started with logging disabled by default. To customize
this, pass `-remote.logfile` to the gopls forwarder. Using
`-remote.logfile=auto`, the daemon will log to a default location (on posix
systems: `/tmp/gopls-daemon-<pid>.log`).
The gopls daemon does not log session-scoped messages: those are instead
reflected back to the forwarder so that they can be accessed by the editor.
Daemon logs will only contain global messages, for example logs when sessions
connect and disconnect.
It is recommended to start the forwarder gopls process with `-rpc.trace`, so
that its logfile will contain rpc trace logs specific to the LSP session.
## Using multiple shared gopls instances
There may be environments where it is desirable to have more than one shared
gopls instance. If managing the daemon manually, this can be done by simply
choosing different `-listen` addresses for each distinct daemon process.
On POSIX systems, there is also support for automatic management of distinct
shared gopls processes: distinct daemons can be selected by passing
`-remote="auto;<id>"`. Any gopls forwarder passing the same value for `<id>`
will use the same shared daemon.
## FAQ
**Q: Why am I not saving as much memory as I expected when using a shared gopls?**
A: As described in [implementation.md](implementation.md), gopls has a concept
of view/session/cache. Each session and view map onto exactly one editor
session (because they contain things like edited but unsaved buffers). The
cache contains things that are independent of any editor session, and can
therefore be shared.
When, for example, three editor session are sharing a single gopls process,
they will share the cache but will each have their own session and view. The
memory savings in this mode, when compared to three separate gopls processes,
corresponds to the amount of cache overlap across sessions.
Because this hasn't mattered much in the past, it is likely that there is state
that can be moved out of the session/view, and into the cache, thereby
increasing the amount of memory savings in the shared mode.
**Q: How do I customize the daemon instance when using `-remote=auto`?**
The daemon may be customized using flags of the form `-remote.*` on the
forwarder gopls. This causes the forwarder to invoke gopls with these settings
when starting the daemon. As of writing, we expose the following configuration:
* `-remote.logfile`: the location of the daemon logfile
* `-remote.debug`: the daemon's debug address
* `-remote.listen.timeout`: the amount of time the daemon should wait for new
connections while there are no current connections, before shutting down. If
`0`, listen indefinitely.
Note that once the daemon is already running, setting these flags will not
change its configuration. These flags only matter for the forwarder process
that actually starts the daemon.