Playbooks 04.3 & 04.6 — Database Initialization & Partitioning
This page covers the two Ansible playbooks responsible for the cyber_intelligence MySQL database lifecycle:
- Playbook 04.3 — initial schema deployment (tables, dictionaries, views, app user)
- Playbook 04.6 — monthly partitioning, retention policy, and the Event Scheduler automation
Schema v3.0 split
Database setup is now a two-step pipeline. Schema v3.0 introduced composite primary keys on three high-volume tables to enable RANGE partitioning — that DDL is created by 04.3, and the partitions / scheduler / maintenance procedures are layered on top by 04.6. Always run them in order.
Related pages: Database Schema · Deployment · Stack & Containers (04.1–2) · Post-Config (04.4) · AI Suite — Ollama (04.5) · Vault & Secrets (06) · Architecture
Pipeline overview
flowchart LR
A[04.2 Stack & Containers<br/>mysql_db running] --> B[04.3 DB create<br/>schema + dictionaries + views]
B --> C[04.4 Post-config]
B --> D[04.6 Partitioning<br/>+ Event Scheduler]
D --> E[Auto rotation<br/>monthly] | Step | Playbook | Source SQL | Outputs |
|---|---|---|---|
| 1 | 04_2_stack.yml | — | mysql_db container running |
| 2 | 04_3_db_create.yml | config/mysql/db_deployment.sql | Database, 8 tables, 7 views, app user |
| 3 | 04_6_setup_partitioning.yml | config/mysql/db_partitioning_retention.sql | Partitions, stored procedures, scheduled events, v_partition_info, partition_maintenance_log |
Playbook 04.3 — Database Initialization
File: ansible/04_3_db_create.yml Hosts: all_servers Privilege escalation: become: yes (sudo)
Initializes the cyber_intelligence MySQL database by rendering the SQL deployment script with real credentials (decrypted by Ansible Vault), executing it inside the running mysql_db container, and securely removing the rendered file from disk. Includes a retry loop to handle cases where MySQL is still starting up when this playbook runs.
04.3 Overview
| Property | Value |
|---|---|
| Playbook file | ansible/04_3_db_create.yml |
| Target hosts | all_servers |
become | Yes (sudo) |
| Source SQL | config/mysql/db_deployment.sql |
| Rendered SQL (transient) | {{ remote_deploy_base }}/init_db_rendered.sql (0600) |
| Execution log | {{ remote_deploy_base }}/mysql_init.log |
| Retries | 10 × 10 s delay (≈100 s budget) |
04.3 Task 4.1 — Render SQL script with actual credentials
Uses ansible.builtin.template to render db_deployment.sql as a Jinja2 template. This replaces the {{ mysql_user }} and {{ vault_mysql_password }} placeholders with values pulled from Vault. The output file is written with mode: '0600' so other system users can't read it.
| ansible/04_3_db_create.yml | |
|---|---|
| Variable | Source | Description |
|---|---|---|
source_mysql_script | group_vars | Path to config/mysql/db_deployment.sql on the control machine |
remote_deploy_base | group_vars | Root deployment directory on the target |
mysql_user | group_vars | Application user name (used by all services) |
vault_mysql_password | Vault (cyber-sentinel/credentials/mysql/app_manager) | Application user password |
04.3 Task 4.2 — Execute SQL and log output
Pipes the rendered SQL into mysql running inside the mysql_db container via docker exec. Output and errors are appended to mysql_init.log with a timestamp header. The retry loop (until + retries: 10) handles situations where the MySQL container is still initializing when this task runs — see Ansible's until documentation.
| Condition | Meaning |
|---|---|
rc == 0 | SQL executed successfully |
ERROR 1007 | Database already exists — idempotent, treated as success |
retries: 10, delay: 10 | Wait up to ≈100 seconds for MySQL to become ready |
Credentials in shell
The MySQL root password is passed as a shell argument (-p'…'). Ansible masks this value in logs because it comes from a Vault variable. The rendered SQL file is also removed in Task 4.3. Never commit init_db_rendered.sql to version control.
04.3 Task 4.3 — Cleanup rendered SQL script
Removes the temporary rendered SQL file from the target server using ansible.builtin.file, ensuring no plaintext credentials remain on disk after the run.
| ansible/04_3_db_create.yml | |
|---|---|
04.3 Full Playbook
Playbook 04.6 — Partitioning & Retention
File: ansible/04_6_setup_partitioning.yml Hosts: all_servers Privilege escalation: become: yes (sudo)
Enables the MySQL Event Scheduler, deploys db_partitioning_retention.sql to create monthly partitions on dns_queries, network_events, and threat_indicators, and verifies that the partitions, stored procedures, and scheduled events are all in place before exiting. See the Database Schema page for the full data-model context.
04.6 Overview
| Property | Value |
|---|---|
| Playbook file | ansible/04_6_setup_partitioning.yml |
| Target hosts | all_servers |
become | Yes (sudo) |
| Source SQL | config/mysql/db_partitioning_retention.sql |
| Setup log | {{ remote_deploy_base }}/partition_setup.log |
| Idempotent | Yes — uses CREATE TABLE IF NOT EXISTS, DROP PROCEDURE IF EXISTS, and INFORMATION_SCHEMA checks |
Dependencies
| Dependency | Why |
|---|---|
| Playbook 04.2 completed | mysql_db container must be running |
| Playbook 04.3 completed | Composite PKs must already exist (schema v3.0+) |
| Vault populated | vault_mysql_root_password required |
Outputs
After a successful run:
- Initial partitions: 4 monthly + 1 future = 5 partitions on each of
dns_queries,network_events,threat_indicators - Stored procedures:
sp_drop_old_partitionsandsp_add_future_partitions - Scheduled events:
evt_drop_old_partitions(02:00 day 1) andevt_add_future_partitions(03:00 day 1) partition_maintenance_logtable for the audit trail- Setup log at
{{ retention_log }}
Pipeline sections
The playbook is organised into seven explicit sections; tasks are numbered hierarchically as [04.6.S.T]:
| Section | Name | Purpose |
|---|---|---|
| 1 | Pre-flight checks | Container running and MySQL accepting connections |
| 2 | Enable Event Scheduler | Toggle event_scheduler = ON if needed |
| 3 | Stage partitioning SQL | Copy SQL script to remote with 0600 perms |
| 4 | Setup partitioning | Execute SQL against cyber_intelligence and tee output |
| 5 | Verification | Count partitions, procedures, events |
| 6 | Cleanup | Remove staged SQL, print operator guidance |
| 7 | Error handling | Append failure marker to log if SQL exec failed |
04.6 Section 1 — Pre-flight checks
Verifies the mysql_db container exists and is responsive before attempting any schema changes. Fast failure here prevents partial state when partitioning runs against a half-started or wrong database.
The mysqladmin ping check returns success only when MySQL is fully accepting connections — not just docker run-ing. Because by stage 04.6 the database has already been up since 04.3, a single check is enough (no retry loop needed unlike Task 4.2).
04.6 Section 2 — Enable Event Scheduler
The MySQL Event Scheduler is OFF by default in many distributions. Without it, CREATE EVENT statements are stored but never fire — partitions would not auto-rotate. The playbook reads the current state and toggles it on if necessary.
Setting is volatile
SET GLOBAL event_scheduler = ON takes effect immediately but does not survive a container restart — for persistence, my.cnf must include event_scheduler=ON. That is out of scope for this playbook and belongs to the stack configuration (04.1–2). Re-running 04.6 after a restart will toggle the scheduler back on idempotently.
04.6 Section 3 — Stage partitioning SQL
Copies db_partitioning_retention.sql to the remote host with restrictive permissions using ansible.builtin.copy. Note that this script is parameterless — no Jinja2 placeholders — so we use copy: rather than template: (which is reserved for 04.3).
| ansible/04_6_setup_partitioning.yml | |
|---|---|
04.6 Section 4 — Setup partitioning
Executes the SQL script against cyber_intelligence with full output captured to partition_setup.log. The tee -a ensures both the log file and Ansible's register capture the output, which is essential for debugging silent failures.
Error 1503: composite PK required
If you see ER_PARTITION_FUNCTION_PRIMARY_KEY (1503), the database was deployed with an old schema that lacks composite primary keys. Re-run 04.3 with the v3.0+ db_deployment.sql. See the Database Schema page for the full PK rationale.
04.6 Section 5 — Verification
After a successful SQL run, the playbook queries INFORMATION_SCHEMA to count what was actually created — partitions, stored procedures, and events — instead of trusting the SQL exit code alone.
| Task | Inspects | Expected after first run |
|---|---|---|
[04.6.5.1] partitions on dns_queries | INFORMATION_SCHEMA.PARTITIONS | 5 (4 monthly + p_future) |
[04.6.5.2] partitions on network_events | same | 5 |
[04.6.5.3] partitions on threat_indicators | same | 5 |
[04.6.5.4] stored procedures | INFORMATION_SCHEMA.ROUTINES | 2 |
[04.6.5.5] scheduled events | INFORMATION_SCHEMA.EVENTS | 2 |
[04.6.5.7] first 10 partition sizes | sample from v_partition_info | rows present |
Example summary printed by [04.6.5.6]:
===== Partitioning Setup Summary =====
Status: SUCCESS
Partitions Created:
- dns_queries: 5 partitions
- network_events: 5 partitions
- threat_indicators: 5 partitions
Automation:
- Stored Procedures: 2
- Scheduled Events: 2
Log Location: {{ remote_deploy_base }}/partition_setup.log
=======================================
04.6 Section 6 — Cleanup
Removes the staged SQL script (no plaintext-secrets concern here — the script has no credentials — but consistent with 04.3) and prints next-step guidance:
Operator follow-up queries documented in the Database Schema page:
-- Audit trail
SELECT * FROM partition_maintenance_log ORDER BY executed_at DESC LIMIT 20;
-- Per-partition size and row counts
SELECT * FROM v_partition_info;
-- Verify scheduler still on
SELECT @@global.event_scheduler;
-- List events
SHOW EVENTS FROM cyber_intelligence;
04.6 Section 7 — Error handling
The whole section is gated on partition_result.rc != 0, so it is a no-op on success. On failure it appends a FAILED marker to the setup log and points the operator at it. The failed_when on [04.6.4.2] aborts the playbook before this section runs in any case — section 7 exists to leave a trace, not to recover.
04.6 Common errors
| Error | Likely cause | Fix |
|---|---|---|
Error 1503: PRIMARY KEY must include all columns in the partitioning function | Database deployed with pre-v3.0 schema (no composite PKs) | Re-run 04.3 with current db_deployment.sql |
Event scheduler is not enabled | Section 2 didn't run, or my.cnf overrides at restart | Verify [04.6.2.2] executed; for persistence add event_scheduler=ON to my.cnf at the 04.1–2 stack stage |
container not running | 04.2 was never run, or container crashed | Section 1 fails fast — restart the stack |
| Partitions already exist on re-run | Expected behaviour | Idempotent — INFORMATION_SCHEMA checks in the SQL procedures skip existing months |
04.6 Variables reference
| Variable | Source | Purpose |
|---|---|---|
main_repo_source_dir | group_vars | Local repo root on control machine |
remote_deploy_base | group_vars | Target deployment directory |
deployment_user | group_vars | Owner of staged files on target |
vault_mysql_root_password | Vault (cyber-sentinel/credentials/mysql/root) | MySQL root password |
retention_script | playbook-local | Path to db_partitioning_retention.sql |
retention_log | playbook-local | Setup log location (partition_setup.log) |