Skip to main content

Database Schema Reference

This document describes every table in the MIH database schema, including column definitions, constraints, indexes, and usage notes.


Overview

MIH uses five tables, all prefixed with local_integrationhub_:

TablePurposeRows (typical)
svcRegistered external services1–50
cbCircuit breaker state (one row per service)Same as svc
logRequest/response log (auto-purged)Up to max_log_entries
rulesEvent Bridge rules1–500
dlqDead Letter Queue for failed events0–∞ (manual cleanup)

local_integrationhub_svc — Services

The primary configuration table. Each row represents one registered external service.

Columns

ColumnTypeNullableDefaultDescription
idBIGINTNoautoPrimary key
nameVARCHAR(255)NoUnique service slug. Used as the lookup key in mih::request(). No spaces.
typeVARCHAR(10)NorestTransport type: rest, amqp, or soap
base_urlVARCHAR(1333)NoBase URL for REST/SOAP, or full AMQP connection string
auth_typeVARCHAR(20)YesnullAuthentication method: bearer or apikey
auth_tokenLONGTEXTYesnullToken or API key value
timeoutBIGINTNo5Request timeout in seconds
max_retriesBIGINTNo3Maximum retry attempts after the first failure
retry_backoffBIGINTNo1Initial backoff in seconds (doubles each retry)
cb_failure_thresholdBIGINTNo5Consecutive failures before opening the circuit
cb_cooldownBIGINTNo30Seconds before attempting recovery (HALFOPEN)
response_queueVARCHAR(255)YesnullAMQP queue name for inbound response consumption
enabledTINYINT(1)No11 = active, 0 = disabled
timecreatedBIGINTNoUnix timestamp of creation
timemodifiedBIGINTNoUnix timestamp of last modification

Indexes

IndexColumnsType
PrimaryidUNIQUE
ix_namenameUNIQUE

Notes

  • The name column is the public identifier used in PHP code. Changing it breaks existing mih::request() calls.
  • auth_token is stored in plaintext. For high-security environments, consider encrypting this column.
  • base_url for AMQP services contains the full connection string including credentials.

local_integrationhub_cb — Circuit Breaker

One row per service. Tracks the circuit breaker state.

Columns

ColumnTypeNullableDefaultDescription
idBIGINTNoautoPrimary key
serviceidBIGINTNoFK → local_integrationhub_svc.id
stateVARCHAR(10)NoclosedCurrent state: closed, open, or halfopen
failure_countBIGINTNo0Consecutive failure counter. Resets to 0 on success.
last_failureBIGINTNo0Unix timestamp of the most recent failure
timemodifiedBIGINTNoUnix timestamp of last state change

Indexes

IndexColumnsType
PrimaryidUNIQUE
ix_serviceidserviceidUNIQUE

Notes

  • Created automatically when a service is registered (registry::create_service())
  • Deleted automatically when the service is deleted
  • failure_count is a consecutive counter — it resets to 0 on any success
  • last_failure is used to calculate when the cooldown expires: time() - last_failure >= cooldown

State Values

stateMeaning
closedNormal operation
openCircuit tripped — requests rejected
halfopenRecovery probe — one request allowed

local_integrationhub_log — Request Log

Records every outbound (and inbound AMQP) request.

Columns

ColumnTypeNullableDefaultDescription
idBIGINTNoautoPrimary key
serviceidBIGINTNoFK → local_integrationhub_svc.id
endpointVARCHAR(1333)YesnullEndpoint path called
http_methodVARCHAR(10)YesnullHTTP method used (GET, POST, etc.)
http_statusBIGINTYesnullHTTP response code. null for AMQP.
latency_msBIGINTNo0Response time in milliseconds
attempt_countBIGINTNo1Total attempts made (including retries)
successTINYINT(1)No01 = success, 0 = failure
error_messageLONGTEXTYesnullError description if failed
directionVARCHAR(10)Nooutboundoutbound (MIH → service) or inbound (service → MIH)
timecreatedBIGINTNoUnix timestamp of the request

Indexes

IndexColumnsType
PrimaryidUNIQUE
ix_serviceidserviceidBTREE
ix_timecreatedtimecreatedBTREE

Auto-Purge

After every INSERT, the MIH API checks the total row count. If it exceeds max_log_entries (default: 500), the oldest rows are deleted:

DELETE FROM local_integrationhub_log
WHERE id NOT IN (
SELECT id FROM local_integrationhub_log
ORDER BY timecreated DESC
LIMIT 500
)

Notes

  • latency_ms includes all retry delays — it is the total wall-clock time from request start to final response
  • attempt_count = 1 means the request succeeded on the first try
  • direction = 'inbound' is used by consume_responses_task for AMQP response messages

local_integrationhub_rules — Event Bridge Rules

Each row maps a Moodle event to a service call.

Columns

ColumnTypeNullableDefaultDescription
idBIGINTNoautoPrimary key
eventnameVARCHAR(255)NoFull PHP class name of the Moodle event
serviceidBIGINTNoFK → local_integrationhub_svc.id
endpointVARCHAR(255)YesnullEndpoint override. For AMQP: routing key. For SOAP: method name.
http_methodVARCHAR(10)NoPOSTHTTP method for REST services
payload_templateLONGTEXTYesnullJSON template with {{variable}} placeholders
enabledTINYINT(1)No11 = active, 0 = disabled
timecreatedBIGINTNoUnix timestamp of creation
timemodifiedBIGINTNoUnix timestamp of last modification

Indexes

IndexColumnsType
PrimaryidUNIQUE
ix_eventnameeventnameBTREE
ix_serviceidserviceidBTREE

Notes

  • The eventname index is critical for performance — the observer queries this table on every Moodle event
  • Multiple rules can target the same event (fan-out: one event → multiple services)
  • Multiple rules can target the same service (fan-in: multiple events → one service)
  • Disabling a rule (enabled = 0) stops dispatch without deleting the configuration

local_integrationhub_dlq — Dead Letter Queue

Stores events that failed to deliver after all retry attempts.

Columns

ColumnTypeNullableDefaultDescription
idBIGINTNoautoPrimary key
eventnameVARCHAR(255)NoEvent class name that failed
serviceidBIGINTNoTarget service ID
payloadLONGTEXTYesnullJSON-encoded payload that was attempted
error_messageLONGTEXTYesnullLast error message from the failed attempt
timecreatedBIGINTNoUnix timestamp when the event was moved to DLQ

Indexes

IndexColumnsType
PrimaryidUNIQUE
ix_serviceidserviceidBTREE
ix_timecreatedtimecreatedBTREE

Notes

  • The DLQ does not auto-purge — entries accumulate until manually deleted
  • Replaying a DLQ entry creates a new dispatch_event_task adhoc task
  • The serviceid may reference a deleted service — always check before replaying

Entity Relationship Diagram

local_integrationhub_svc
│ id (PK)
│ name (UNIQUE)
│ type
│ base_url
│ ...

├──────────────────────────────────────────────────────────────────┐
│ │
▼ (1:1) ▼ (1:N)
local_integrationhub_cb local_integrationhub_log
serviceid (FK, UNIQUE) serviceid (FK)
state endpoint
failure_count http_status
last_failure latency_ms
success

▼ (1:N)
local_integrationhub_rules
serviceid (FK)
eventname
payload_template
enabled


▼ (1:N, on failure)
local_integrationhub_dlq
serviceid (FK)
eventname
payload
error_message

Useful Queries

Services with open circuits

SELECT s.name, cb.state, cb.failure_count, cb.last_failure
FROM local_integrationhub_svc s
JOIN local_integrationhub_cb cb ON cb.serviceid = s.id
WHERE cb.state != 'closed'
ORDER BY cb.last_failure DESC;

Error rate per service (last 24h)

SELECT
s.name,
COUNT(*) AS total,
SUM(CASE WHEN l.success = 0 THEN 1 ELSE 0 END) AS errors,
AVG(l.latency_ms) AS avg_latency_ms
FROM local_integrationhub_log l
JOIN local_integrationhub_svc s ON s.id = l.serviceid
WHERE l.timecreated > UNIX_TIMESTAMP() - 86400
GROUP BY s.id, s.name
ORDER BY errors DESC;

DLQ entries by service

SELECT s.name, COUNT(*) AS dlq_count, MAX(d.timecreated) AS last_failure
FROM local_integrationhub_dlq d
JOIN local_integrationhub_svc s ON s.id = d.serviceid
GROUP BY s.id, s.name
ORDER BY dlq_count DESC;

Active rules per event

SELECT eventname, COUNT(*) AS rule_count
FROM local_integrationhub_rules
WHERE enabled = 1
GROUP BY eventname
ORDER BY rule_count DESC;