We want multiple tools to be able to share the Rubydex analysis graph. This comes with 2 issues:
- Each tool currently needs to pay the price of indexing + resolving the graph (boot time cost)
- Each tool holding its own graph pays the full memory price, which for large codebases can be prohibitive
We need a way of sharing the graph that hopefully allows both for eliminating the need to pay the boot cost for each tool, but also that allows us to not grow the memory usage linearly with each tool.
Ideas
Database backed cache
We might be able to take a strategy similar to Pyre check and cache the graph into a database. By tracing the graph, we should be able to partially load data from the database, so that each tools only pulls what they need and the memory growth isn't linear.
Saving boot time is also directly related to the ability of partially loading data. When launching the analysis, we need to be able to compare the previous state of the workspace (cached data) with the current state (i.e.: which documents were modified between runs). By identifying the set of documents that got modified, we can then load only the data required to analyze them, which neatly covers the use cases for linters, LSPs, MCPs, and really most CLI tools.
Pyre check uses a SQLite database with a sparse cache that remembers the list of documents that each document depends on, which allows for easy loading based on the set that got modified. The challenging parts come from the semantic differences between Ruby and Python since a lot of Ruby's data is global, which makes nailing this approach a bit challenging.
We should also experiment with ladybug DB, which is a graph database that might provide us with exactly what we need to trace the graph in the database and load only the parts that are required.
Client/server architecture
This idea means having a background process (server) that runs the analysis, while tools are clients that can query the server for information. This solves the memory sharing aspect, but there are other concerns that we would need to investigate:
- How would a tool perform non-committed changes to the analysis without accidentally propagating changes too early to other tools? For example, an LSP handling changes that weren't committed to disk yet
- While the memory sharing aspect is solved, the boot time cost isn't and we may still add some form of cache to speed up booting regardless
- Communicating to the external process will probably require some form of IO and serialization, which could prove costly for query-heavy consumers like a type inference algorithm
If loading partial data proves successful, the last point may not be a concern as the tool can load all of the data upfront and then perform the analysis in-memory.
We want multiple tools to be able to share the Rubydex analysis graph. This comes with 2 issues:
We need a way of sharing the graph that hopefully allows both for eliminating the need to pay the boot cost for each tool, but also that allows us to not grow the memory usage linearly with each tool.
Ideas
Database backed cache
We might be able to take a strategy similar to Pyre check and cache the graph into a database. By tracing the graph, we should be able to partially load data from the database, so that each tools only pulls what they need and the memory growth isn't linear.
Saving boot time is also directly related to the ability of partially loading data. When launching the analysis, we need to be able to compare the previous state of the workspace (cached data) with the current state (i.e.: which documents were modified between runs). By identifying the set of documents that got modified, we can then load only the data required to analyze them, which neatly covers the use cases for linters, LSPs, MCPs, and really most CLI tools.
Pyre check uses a SQLite database with a sparse cache that remembers the list of documents that each document depends on, which allows for easy loading based on the set that got modified. The challenging parts come from the semantic differences between Ruby and Python since a lot of Ruby's data is global, which makes nailing this approach a bit challenging.
We should also experiment with ladybug DB, which is a graph database that might provide us with exactly what we need to trace the graph in the database and load only the parts that are required.
Client/server architecture
This idea means having a background process (server) that runs the analysis, while tools are clients that can query the server for information. This solves the memory sharing aspect, but there are other concerns that we would need to investigate:
If loading partial data proves successful, the last point may not be a concern as the tool can load all of the data upfront and then perform the analysis in-memory.