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: `fetch_initial_posts` option
|
||||||
- Configuration: `notify_email` option
|
- Configuration: `notify_email` option
|
||||||
- Pleroma API: User subscribtions
|
- Pleroma API: User subscribtions
|
||||||
|
- Pleroma API: Healthcheck endpoint
|
||||||
- Admin API: Endpoints for listing/revoking invite tokens
|
- Admin API: Endpoints for listing/revoking invite tokens
|
||||||
- Admin API: Endpoints for making users follow/unfollow each other
|
- Admin API: Endpoints for making users follow/unfollow each other
|
||||||
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
- 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
|
* `remote`: BOOLEAN field, receives notifications from people on remote instances
|
||||||
* `local`: BOOLEAN field, receives notifications from people on the local instance
|
* `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"}`
|
* 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)
|
post("/password_reset", UtilController, :password_reset)
|
||||||
get("/emoji", UtilController, :emoji)
|
get("/emoji", UtilController, :emoji)
|
||||||
get("/captcha", UtilController, :captcha)
|
get("/captcha", UtilController, :captcha)
|
||||||
|
get("/healthcheck", UtilController, :healthcheck)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma", Pleroma.Web do
|
scope "/api/pleroma", Pleroma.Web do
|
||||||
|
|
|
@ -363,4 +363,17 @@ def delete_account(%{assigns: %{user: user}} = conn, params) do
|
||||||
def captcha(conn, _params) do
|
def captcha(conn, _params) do
|
||||||
json(conn, Pleroma.Captcha.new())
|
json(conn, Pleroma.Captcha.new())
|
||||||
end
|
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
|
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"
|
assert html_response(response, 200) =~ "Log in to follow"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "GET /api/pleroma/healthcheck", %{conn: conn} do
|
||||||
|
conn = get(conn, "/api/pleroma/healthcheck")
|
||||||
|
|
||||||
|
assert conn.status in [200, 503]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue