forked from AkkomaGang/akkoma
Feature/826 healthcheck endpoint
This commit is contained in:
parent
7fcbda702e
commit
88f0be9693
7 changed files with 120 additions and 0 deletions
|
@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Configuration: `fetch_initial_posts` option
|
||||
- Configuration: `notify_email` option
|
||||
- Pleroma API: User subscribtions
|
||||
- Pleroma API: Healthcheck endpoint
|
||||
- Admin API: Endpoints for listing/revoking invite tokens
|
||||
- Admin API: Endpoints for making users follow/unfollow each other
|
||||
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
||||
|
|
|
@ -197,3 +197,20 @@ See [Admin-API](Admin-API.md)
|
|||
* `remote`: BOOLEAN field, receives notifications from people on remote instances
|
||||
* `local`: BOOLEAN field, receives notifications from people on the local instance
|
||||
* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
|
||||
|
||||
## `/api/pleroma/healthcheck`
|
||||
### Healthcheck endpoint with additional system data.
|
||||
* Method `GET`
|
||||
* Authentication: not required
|
||||
* Params: none
|
||||
* Response: JSON, statuses (200 - healthy, 503 unhealthy).
|
||||
* Example response:
|
||||
```json
|
||||
{
|
||||
"pool_size": 0, # database connection pool
|
||||
"active": 0, # active processes
|
||||
"idle": 0, # idle processes
|
||||
"memory_used": 0.00, # Memory used
|
||||
"healthy": true # Instance state
|
||||
}
|
||||
```
|
||||
|
|
60
lib/healthcheck.ex
Normal file
60
lib/healthcheck.ex
Normal file
|
@ -0,0 +1,60 @@
|
|||
defmodule Pleroma.Healthcheck do
|
||||
@moduledoc """
|
||||
Module collects metrics about app and assign healthy status.
|
||||
"""
|
||||
alias Pleroma.Healthcheck
|
||||
alias Pleroma.Repo
|
||||
|
||||
defstruct pool_size: 0,
|
||||
active: 0,
|
||||
idle: 0,
|
||||
memory_used: 0,
|
||||
healthy: true
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
pool_size: non_neg_integer(),
|
||||
active: non_neg_integer(),
|
||||
idle: non_neg_integer(),
|
||||
memory_used: number(),
|
||||
healthy: boolean()
|
||||
}
|
||||
|
||||
@spec system_info() :: t()
|
||||
def system_info do
|
||||
%Healthcheck{
|
||||
memory_used: Float.round(:erlang.memory(:total) / 1024 / 1024, 2)
|
||||
}
|
||||
|> assign_db_info()
|
||||
|> check_health()
|
||||
end
|
||||
|
||||
defp assign_db_info(healthcheck) do
|
||||
database = Application.get_env(:pleroma, Repo)[:database]
|
||||
|
||||
query =
|
||||
"select state, count(pid) from pg_stat_activity where datname = '#{database}' group by state;"
|
||||
|
||||
result = Repo.query!(query)
|
||||
pool_size = Application.get_env(:pleroma, Repo)[:pool_size]
|
||||
|
||||
db_info =
|
||||
Enum.reduce(result.rows, %{active: 0, idle: 0}, fn [state, cnt], states ->
|
||||
if state == "active" do
|
||||
Map.put(states, :active, states.active + cnt)
|
||||
else
|
||||
Map.put(states, :idle, states.idle + cnt)
|
||||
end
|
||||
end)
|
||||
|> Map.put(:pool_size, pool_size)
|
||||
|
||||
Map.merge(healthcheck, db_info)
|
||||
end
|
||||
|
||||
@spec check_health(Healthcheck.t()) :: Healthcheck.t()
|
||||
def check_health(%{pool_size: pool_size, active: active} = check)
|
||||
when active >= pool_size do
|
||||
%{check | healthy: false}
|
||||
end
|
||||
|
||||
def check_health(check), do: check
|
||||
end
|
|
@ -135,6 +135,7 @@ defmodule Pleroma.Web.Router do
|
|||
post("/password_reset", UtilController, :password_reset)
|
||||
get("/emoji", UtilController, :emoji)
|
||||
get("/captcha", UtilController, :captcha)
|
||||
get("/healthcheck", UtilController, :healthcheck)
|
||||
end
|
||||
|
||||
scope "/api/pleroma", Pleroma.Web do
|
||||
|
|
|
@ -363,4 +363,17 @@ def delete_account(%{assigns: %{user: user}} = conn, params) do
|
|||
def captcha(conn, _params) do
|
||||
json(conn, Pleroma.Captcha.new())
|
||||
end
|
||||
|
||||
def healthcheck(conn, _params) do
|
||||
info = Pleroma.Healthcheck.system_info()
|
||||
|
||||
conn =
|
||||
if info.healthy do
|
||||
conn
|
||||
else
|
||||
Plug.Conn.put_status(conn, :service_unavailable)
|
||||
end
|
||||
|
||||
json(conn, info)
|
||||
end
|
||||
end
|
||||
|
|
22
test/healthcheck_test.exs
Normal file
22
test/healthcheck_test.exs
Normal file
|
@ -0,0 +1,22 @@
|
|||
defmodule Pleroma.HealthcheckTest do
|
||||
use Pleroma.DataCase
|
||||
alias Pleroma.Healthcheck
|
||||
|
||||
test "system_info/0" do
|
||||
result = Healthcheck.system_info() |> Map.from_struct()
|
||||
|
||||
assert Map.keys(result) == [:active, :healthy, :idle, :memory_used, :pool_size]
|
||||
end
|
||||
|
||||
describe "check_health/1" do
|
||||
test "pool size equals active connections" do
|
||||
result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 10})
|
||||
refute result.healthy
|
||||
end
|
||||
|
||||
test "chech_health/1" do
|
||||
result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 9})
|
||||
assert result.healthy
|
||||
end
|
||||
end
|
||||
end
|
|
@ -245,4 +245,10 @@ test "show follow account page if the `acct` is a account link", %{conn: conn} d
|
|||
assert html_response(response, 200) =~ "Log in to follow"
|
||||
end
|
||||
end
|
||||
|
||||
test "GET /api/pleroma/healthcheck", %{conn: conn} do
|
||||
conn = get(conn, "/api/pleroma/healthcheck")
|
||||
|
||||
assert conn.status in [200, 503]
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue