Definition & Overview
Uptime is the percentage of time a system or service is operational and accessible to its users, calculated as the ratio of available time to total time within a measurement period. Expressed as a percentage with increasing precision, uptime targets define service reliability expectations: 99% uptime allows 3.65 days of downtime per year, 99.9% (three nines) allows 8.76 hours, 99.99% (four nines) allows 52.6 minutes, and 99.999% (five nines) allows just 5.26 minutes of annual downtime.
Uptime is the most visible metric of service reliability and forms the foundation of Service Level Agreements (SLAs). However, raw uptime numbers can be misleading without context. A service might report 99.9% uptime while experiencing frequent 30-second outages that collectively fit within the budget but severely impact user experience. Modern reliability engineering supplements uptime with more nuanced metrics like error budgets, latency percentiles, and success rate to provide a comprehensive picture of service health.
The Prismatic Platform, deployed on Fly.io with staging (prismatic-staging.fly.dev) and production (prismatic-prod.fly.dev) environments, monitors uptime through health check endpoints that verify not just HTTP connectivity but also critical dependency availability (PostgreSQL, ETS tables, OSINT tool registry). The Ollama local AI integration targets >99% uptime for inference availability, with automatic cloud fallback when local models are unavailable.
Technical Deep Dive
The platform implements health checks that provide granular uptime visibility:
defmodule PrismaticWeb.HealthController do
@moduledoc """
Health check endpoint that verifies system component
availability for uptime monitoring.
"""
use PrismaticWeb, :controller
@health_checks [
{:database, &__MODULE__.check_database/0},
{:ets_registry, &__MODULE__.check_ets_registry/0},
{:osint_tools, &__MODULE__.check_osint_tools/0},
{:memory, &__MODULE__.check_memory/0}
]
@spec check(Plug.Conn.t(), map()) :: Plug.Conn.t()
def check(conn, _params) do
start = System.monotonic_time(:microsecond)
results =
@health_checks
|> Task.async_stream(fn {name, check_fn} ->
{name, timed_check(check_fn)}
end, timeout: 5_000, max_concurrency: 4)
|> Enum.map(fn {:ok, result} -> result end)
|> Map.new()
duration_us = System.monotonic_time(:microsecond) - start
all_healthy = Enum.all?(results, fn {_, {status, _}} -> status == :ok end)
response = %{
status: if(all_healthy, do: "healthy", else: "degraded"),
checks: format_checks(results),
response_time_us: duration_us,
timestamp: DateTime.utc_now(),
version: Application.spec(:prismatic, :vsn) |> to_string()
}
status_code = if all_healthy, do: 200, else: 503
conn
|> put_status(status_code)
|> json(response)
end
def check_database do
case Ecto.Adapters.SQL.query(PrismaticDd.Repo, "SELECT 1", []) do
{:ok, _} -> {:ok, "connected"}
{:error, reason} -> {:error, inspect(reason)}
end
end
def check_ets_registry do
case :ets.info(:osint_tool_registry) do
:undefined -> {:error, "registry not initialized"}
info -> {:ok, "#{Keyword.get(, :size, 0)} tools registered"}
end
end
def check_osint_tools do
count = PrismaticOsintCore.ToolRegistry.count()
if count > 0, do: {:ok, "#{} tools available"}, else: {:error, "no tools registered"}
end
def check_memory do
memory = :erlang.memory(:total)
limit = 2_000_000_000
if memory < limit do
{:ok, "#{div(, 1_048_576)}MB used"}
else
{:error, "memory pressure: #{div(, 1_048_576)}MB"}
end
end
defp timed_check(check_fn) do
start = System.monotonic_time(:microsecond)
result = check_fn.()
duration = System.monotonic_time(:microsecond) - start
{elem(result, 0), %{message: elem(result, 1), duration_us: duration}}
end
defp format_checks(results) do
Map.new(results, fn {name, {status, details}} ->
{name, Map.put(details, :status, status)}
end)
end
end
For uptime tracking and SLA calculation:
defmodule PrismaticMonitoring.UptimeTracker do
@moduledoc """
Tracks service uptime over time windows for SLA
calculation and availability reporting.
"""
use GenServer
@check_interval_ms 30_000
@health_url "http://localhost:4000/api/v1/health"
defstruct [:checks, :started_at, total_checks: 0, successful_checks: 0]
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@impl true
def init(_opts) do
schedule_check()
{:ok, %__MODULE__{checks: [], started_at: DateTime.utc_now()}}
end
@spec current_uptime() :: float()
def current_uptime do
GenServer.call(__MODULE__, :get_uptime)
end
@spec uptime_for_period(pos_integer()) :: float()
def uptime_for_period(hours) do
GenServer.call(__MODULE__, {:get_uptime_period, hours})
end
@impl true
def handle_call(:get_uptime, _from, state) do
uptime =
if state.total_checks > 0 do
Float.round(state.successful_checks / state.total_checks * 100, 4)
else
100.0
end
{:reply, uptime, state}
end
@impl true
def handle_call({:get_uptime_period, hours}, _from, state) do
cutoff = DateTime.add(DateTime.utc_now(), -hours * 3600, :second)
relevant = Enum.filter(state.checks, fn {ts, _} ->
DateTime.compare(ts, cutoff) == :gt
end)
uptime =
if Enum.empty?(relevant) do
100.0
else
successful = Enum.count(relevant, fn {_, status} -> status == :ok end)
Float.round(successful / length(relevant) * 100, 4)
end
{:reply, uptime, state}
end
@impl true
def handle_info(:check, state) do
status = perform_health_check()
now = DateTime.utc_now()
new_state = %{state |
checks: [{now, status} | Enum.take(state.checks, 86_400)],
total_checks: state.total_checks + 1,
successful_checks: state.successful_checks + (if status == :ok, do: 1, else: 0)
}
schedule_check()
{:noreply, new_state}
end
defp perform_health_check do
case :httpc.request(:get, {String.to_charlist(@health_url), []}, [timeout: 5_000], []) do
{:ok, {{_, 200, _}, _, _}} -> :ok
_ -> :error
end
end
defp schedule_check do
Process.send_after(self(), :check, @check_interval_ms)
end
endArchitecture & Implementation
The platform's uptime monitoring architecture operates at multiple levels:
Infrastructure Level: Fly.io provides built-in health checks that monitor the application's HTTP endpoints. When a health check fails, Fly.io automatically restarts the instance and routes traffic to healthy replicas.
Application Level: The health controller checks all critical dependencies (database, ETS registries, memory) within a 10ms budget. This fast health check satisfies the platform's P0 performance standard while providing meaningful degradation signals.
Component Level: Individual GenServers and supervisors report their health status. The PrismaticSupervisor's HealthMonitor aggregates component-level health into a system-wide status that feeds the health check endpoint.
External Monitoring: Third-party monitoring services ping the production and staging endpoints at regular intervals, providing independent uptime verification that isn't subject to the same failure modes as internal monitoring.
Usage in Prismatic Platform
The uptime data feeds into the platform's operational dashboards and SLA reporting:
defmodule PrismaticWeb.StatusLive do
use PrismaticWeb, :live_view
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
:timer.send_interval(30_000, :refresh)
end
{:ok, assign_status(socket)}
end
@impl true
def handle_info(:refresh, socket) do
{:noreply, assign_status(socket)}
end
defp assign_status(socket) do
assign(socket,
uptime_24h: PrismaticMonitoring.UptimeTracker.uptime_for_period(24),
uptime_7d: PrismaticMonitoring.UptimeTracker.uptime_for_period(168),
uptime_30d: PrismaticMonitoring.UptimeTracker.uptime_for_period(720),
current_status: PrismaticMonitoring.UptimeTracker.current_uptime()
)
end
endCross-References
- Monitoring - Observation systems tracking uptime
- Health Check - Endpoint for availability verification
- SLA - Service Level Agreement defining uptime targets
- Time to First Byte - Performance metric during uptime
- Warmup - Initialization affecting availability after restart
Created by Tomas Korcak (korczis) | GitHub