https://github.com/j3rn/saturn
A child-eating monster; a library to help you find and eliminate N+1 queries.
https://github.com/j3rn/saturn
Last synced: about 1 year ago
JSON representation
A child-eating monster; a library to help you find and eliminate N+1 queries.
- Host: GitHub
- URL: https://github.com/j3rn/saturn
- Owner: J3RN
- License: mit
- Created: 2025-02-10T14:25:53.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-02-10T17:45:44.000Z (over 1 year ago)
- Last Synced: 2025-04-08T14:46:52.616Z (over 1 year ago)
- Language: Elixir
- Size: 23.4 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Saturn
A child-eating monster; a library to help you find and eliminate N+1 queries.
## Usage
In order for Saturn to collect query data, it first must be [installed](#installation). Once that is completed, you may use the following functions:
By default, Saturn starts disabled (i.e. not collecting query data) to save memory. When you'd like to start collecting query data, run:
```elixir
Saturn.enable()
```
You can also configure Saturn to be enabled at start-up using the `enable` key of its configuration:
```elixir
config :saturn, enable: true
```
To stop collecting query data, run:
```elixir
Saturn.disable()
```
Once you have collected some data, you can query the most-made queries:
```elixir
iex> Saturn.report()
Query: "SELECT DISTINCT o0.\"queue\" FROM \"public\".\"oban_jobs\" AS o0 WHERE (o0.\"state\" = 'available') AND (NOT (o0.\"queue\" IS NULL))"
Count: 100
Stacktrace:
lib/ecto/repo/supervisor.ex:162: Ecto.Repo.Supervisor.tuplet/2
lib/my_app/repo.ex:2: MyApp.Repo.all/2
lib/oban/plugins/stager.ex:131: Oban.Plugins.Stager.notify_queues/1
lib/oban/plugins/stager.ex:98: Oban.Plugins.Stager.-check_leadership_and_stage/1-fun-0-/1
lib/ecto/adapters/sql.ex:1202: Ecto.Adapters.SQL.-checkout_or_transaction/4-fun-0-/3
...
#=> :ok
```
Invoking `Saturn.report()` as `Saturn.report(:count)` is equivalent; the argument defaults to `:count`.
Saturn also supports querying by time:
```elixir
iex> Saturn.report(:time)
Query: "SELECT DISTINCT o0.\"queue\" FROM \"public\".\"oban_jobs\" AS o0 WHERE (o0.\"state\" = 'available') AND (NOT (o0.\"queue\" IS NULL))"
Time: 157 ms
Stacktrace:
lib/ecto/repo/supervisor.ex:162: Ecto.Repo.Supervisor.tuplet/2
lib/my_app/repo.ex:2: MyApp.Repo.all/2
lib/oban/plugins/stager.ex:131: Oban.Plugins.Stager.notify_queues/1
lib/oban/plugins/stager.ex:98: Oban.Plugins.Stager.-check_leadership_and_stage/1-fun-0-/1
lib/ecto/adapters/sql.ex:1202: Ecto.Adapters.SQL.-checkout_or_transaction/4-fun-0-/3
...
#=> :ok
```
Lastly, Saturn supports a prof-style output (a la `eprof`, `fprof`, etc) to help you identify cost centers:
```elixir
iex> Saturn.report(:prof)
Source Count %Count Time %Time
Saturn.fake/1 2 66 246.91 95
SELECT * FROM users; 2 66 246.91 95
Saturn.foobar/2 1 33 12.35 4
Saturn.Foobar.do_thing/3 1 33 12.35 4
SELECT * FROM users WHERE id = 5; 1 33 12.35 4
```
If you want to clear all recorded queries, invoke `Saturn.clear/0`:
```elixir
Saturn.clear()
#=> :ok
```
## Installation
The package can be installed by adding `saturn` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:saturn, git: "git@github.com:instinctscience/saturn.git"}
]
end
```
Next, your application needs to be configured to send Ecto telemetry events to Saturn:
```elixir
# lib/myapp_web/telemetry.ex
:ok =
:telemetry.attach(
"saturn-aggregator",
[:myapp, :repo, :query],
&Saturn.handle_query/4,
nil
)
```
**Remember to change `:myapp` to the actual name of your app!**
Finally, in order for Saturn to collect and report stacktraces, Ecto needs to be configured to include stacktraces in its telemetry events:
```elixir
# config/dev.exs
config :myapp, :repo,
username: "postgres",
password: "postgres",
# etc
stacktrace: true
```