Skip to content

Unit Testing Milestone 5: Long-Term Architectural Improvements for Testability #5481

@renecannao

Description

@renecannao

Overview

This milestone captures structural changes to the ProxySQL codebase that make it inherently more testable over time. Unlike Milestones 2–4 which work around the current architecture, this milestone gradually evolves the architecture itself.

These changes are applied opportunistically — when code is already being modified for features or bug fixes — not as a dedicated refactoring project.

Motivation

The current architecture has 25 global singletons accessed directly throughout the codebase (647+ references). While Milestones 2–4 work around this via stubs and extraction, the root cause remains: tight coupling through global state makes it impossible to test many components without scaffolding the entire system.

Long-term, reducing this coupling improves not just testability but also:

  • Debuggability — easier to reason about data flow when dependencies are explicit
  • Parallelism — explicit dependencies enable safer concurrent development
  • Modularity — components can be reused, replaced, or versioned independently

Candidate Improvements

These are directional — specific scoping will depend on learnings from Milestones 2–4:

1. Dependency Injection for Glo* Singletons

Pass dependencies explicitly via constructors or method parameters instead of reaching for globals. Applied gradually:

  • New code uses DI from the start
  • Existing code is retrofitted only when already being modified
  • Globals remain as a compatibility layer during transition

2. Interface Extraction

Define abstract interfaces (pure virtual base classes) for major components:

  • IHostGroupsManager — server selection and pool management
  • IQueryProcessor — rule matching
  • IAuthentication — credential lookup
  • IMonitor — health checking

Tests can then provide mock implementations. Production code uses the real implementations.

3. Break Up proxysql.h

The mega-header forces every translation unit to depend on everything. Splitting it into focused headers (e.g., proxysql_types.h, proxysql_enums.h, proxysql_forward_decls.h) would:

  • Reduce compilation times
  • Allow test files to include only what they need
  • Make dependency relationships explicit

4. Separate proxysql_structs.h

At ~29,000 lines with thread-local variables, forward declarations, enums, and struct definitions all mixed together, this file is a bottleneck. Splitting by concern would improve build times and testability.

5. CI Integration

  • Dedicated CI job for unit tests (runs in seconds, no Docker)
  • Unit tests gate PR merging before heavy E2E tests run
  • Coverage reporting for unit-tested code paths
  • ASAN/TSAN runs for unit tests on every PR

Principles

  • Opportunistic, not speculative — make these changes when touching code for other reasons
  • Backwards compatible — existing code and tests continue to work during transition
  • Measurable — track test coverage, build times, and test execution times to demonstrate value
  • No big bang — each change is small, reviewable, and independently valuable

Prerequisites

  • Milestones 2–4 substantially complete
  • Clear evidence of which architectural constraints are the biggest testing bottlenecks
  • Team alignment on the value of these changes vs. their cost

Relationship to Other Milestones

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions