TypedEctoSchema is an Elixir library that eliminates the gap between your Ecto schema definitions and your typespecs. By generating both at once from a single declaration, it catches type mismatches at compile time rather than at runtime in production. The schemas TypedEctoSchema generates power every query your app makes — when the database layer they wrap becomes unhealthy, the result is failed reads, dropped writes, and inconsistent data. Vigilmon gives you health monitoring, query latency alerting, and heartbeat checks so database failures are caught before they cascade into user-facing errors.
What You'll Set Up
- HTTP health endpoint that validates TypedEctoSchema-driven queries
- Pool utilisation monitoring to catch connection exhaustion
- Cron heartbeat for scheduled Ecto operations
- SSL certificate monitoring for database connections
Prerequisites
- Elixir 1.14+ with
typed_ecto_schemaandecto_sqlinmix.exs - At least one TypedEctoSchema schema used in a production query path
- A free Vigilmon account
Step 1: Add a Health Endpoint That Runs a Live Query
Your health check should exercise a real TypedEctoSchema-backed query so Vigilmon detects both application crashes and database connectivity failures:
# lib/my_app_web/controllers/health_controller.ex
defmodule MyAppWeb.HealthController do
use MyAppWeb, :controller
import Ecto.Query
alias MyApp.Repo
alias MyApp.Accounts.User
def index(conn, _params) do
checks = %{
database: check_database(),
schema_query: check_schema_query()
}
status = if Enum.all?(checks, fn {_, v} -> v == :ok end), do: 200, else: 503
conn
|> put_status(status)
|> json(%{
status: (if status == 200, do: "ok", else: "error"),
checks: checks
})
end
defp check_database do
case Ecto.Adapters.SQL.query(Repo, "SELECT 1", []) do
{:ok, _} -> :ok
{:error, _} -> :error
end
end
defp check_schema_query do
# Run a lightweight query against a TypedEctoSchema-backed table
try do
_ = Repo.one(from u in User, select: u.id, limit: 1)
:ok
rescue
_ -> :error
end
end
end
A typical TypedEctoSchema definition it validates against:
# lib/my_app/accounts/user.ex
defmodule MyApp.Accounts.User do
use TypedEctoSchema
typed_schema "users" do
field :email, :string
field :role, :string
field :active, :boolean, default: true
timestamps()
end
end
Wire the route and verify:
curl http://localhost:4000/health
# {"status":"ok","checks":{"database":"ok","schema_query":"ok"}}
Step 2: Add the Monitor in Vigilmon
- Log in to vigilmon.online and click Add Monitor.
- Set Type to
HTTP / HTTPS. - Enter your health URL:
https://myapp.example.com/health. - Set Check interval to
1 minute. - Set Expected HTTP status to
200. - Under Response body, enable Contains keyword and enter
"ok". - Click Save.
Step 3: Expose Connection Pool Metrics
TypedEctoSchema schemas drive every query, so pool exhaustion is the most common failure mode. Expose pool metrics in your health endpoint:
defmodule MyAppWeb.HealthController do
use MyAppWeb, :controller
import Ecto.Query
alias MyApp.{Repo, Accounts.User}
@pool_warn_threshold 0.8
def index(conn, _params) do
pool_size = Application.get_env(:my_app, Repo)[:pool_size] || 10
pool_status = check_pool_health(pool_size)
db_status = check_database()
query_status = check_schema_query()
all_ok = pool_status == :ok and db_status == :ok and query_status == :ok
http_status = if all_ok, do: 200, else: 503
conn
|> put_status(http_status)
|> json(%{
status: (if all_ok, do: "ok", else: "error"),
database: db_status,
schema_query: query_status,
pool: pool_status,
pool_size: pool_size
})
end
defp check_pool_health(pool_size) do
# DBConnection exposes checked-out count via process dictionary
# For a rough check, verify we can acquire a connection quickly
case Ecto.Adapters.SQL.query(Repo, "SELECT 1", [], timeout: 500) do
{:ok, _} -> :ok
{:error, %DBConnection.ConnectionError{}} -> :exhausted
{:error, _} -> :error
end
end
defp check_database do
case Ecto.Adapters.SQL.query(Repo, "SELECT 1", []) do
{:ok, _} -> :ok
_ -> :error
end
end
defp check_schema_query do
try do
_ = Repo.one(from u in User, select: u.id, limit: 1)
:ok
rescue
_ -> :error
end
end
end
Vigilmon alerts immediately when the pool returns "exhausted" rather than waiting for a complete outage.
Step 4: Instrument Ecto Query Telemetry
TypedEctoSchema-backed schemas emit Ecto Telemetry events for every query. Attach a handler to track slow queries:
# In your Application.start/2
:telemetry.attach(
"ecto-query-monitor",
[:my_app, :repo, :query],
fn _event, %{total_time: total_ns}, %{query: query}, _config ->
total_ms = System.convert_time_unit(total_ns, :nanosecond, :millisecond)
if total_ms > 500 do
require Logger
Logger.warning("Slow Ecto query (#{total_ms}ms): #{String.slice(query, 0, 120)}")
end
end,
nil
)
Add the Ecto telemetry configuration in your Repo:
# config/config.exs
config :my_app, MyApp.Repo,
telemetry_prefix: [:my_app, :repo]
For production, expose per-query latency percentiles in a /metrics endpoint (using telemetry_metrics and telemetry_metrics_prometheus) and add a second Vigilmon HTTP monitor that checks the Prometheus scrape endpoint is reachable.
Step 5: Heartbeat for Scheduled Ecto Operations
If your app runs scheduled jobs that write through TypedEctoSchema-backed schemas — data cleanup, aggregation tasks, export pipelines — add a heartbeat:
- In Vigilmon, click Add Monitor → Cron Heartbeat.
- Set Expected ping interval to your job frequency (e.g.
1440minutes for a daily job). - Copy the heartbeat URL.
defmodule MyApp.CleanupJob do
require Logger
alias MyApp.{Repo, Audit.EventLog}
import Ecto.Query
def run do
cutoff = DateTime.add(DateTime.utc_now(), -30, :day)
{deleted, _} =
Repo.delete_all(
from e in EventLog,
where: e.inserted_at < ^cutoff
)
Logger.info("Deleted #{deleted} stale event log entries")
ping_vigilmon()
end
defp ping_vigilmon do
url = System.get_env("VIGILMON_CLEANUP_HEARTBEAT_URL")
if url, do: :httpc.request(:get, {String.to_charlist(url), []}, [], [])
end
end
If the job fails before the ping, Vigilmon alerts after the window expires.
Step 6: Monitor the Database SSL Certificate
Most production Ecto configurations connect over TLS. Monitor the certificate separately from the application health check:
- Open your Vigilmon project and click Add Monitor → HTTP / HTTPS.
- Enter the HTTPS URL of your app (not just
/health) to get the certificate from the application's TLS terminator. - Enable Monitor SSL certificate.
- Set Alert when certificate expires in less than
21 days.
Alternatively, monitor the database host directly if your provider exposes a status URL. Add your database replica endpoint as a second monitor — many Aurora or Cloud SQL setups have separate read replica endpoints that can go stale independently.
Summary
| Monitor | Target | What It Catches |
|---|---|---|
| HTTP health check | /health on your app | Database down, Ecto query failures |
| Response keyword | "ok" in body | Schema query degraded despite 200 response |
| Pool timeout check | Short-timeout SELECT 1 | Connection pool exhaustion |
| Cron heartbeat | Heartbeat URL | Scheduled Ecto write jobs silently failing |
| SSL certificate | App TLS endpoint | Certificate expiry breaking DB connections |
| Ecto telemetry | Query event handler | Slow queries approaching timeout thresholds |
TypedEctoSchema makes your database schemas more correct at compile time — Vigilmon makes sure the runtime database they point to stays healthy. With a schema-exercising health endpoint, connection pool monitoring, and heartbeats for scheduled writes, you get full observability over the data layer.