Skip to content

chore(release): Prepare v2.3.0 - #44

Merged
soulevilx merged 4 commits into
masterfrom
release/2.3.0
Aug 3, 2026
Merged

chore(release): Prepare v2.3.0#44
soulevilx merged 4 commits into
masterfrom
release/2.3.0

Conversation

@soulevilx

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • Required CI green on this PR (composer ci path via workflows)
  • No unresolved AI/review threads
  • After merge to master: tag v2.3.0, verify GitHub Release + Packagist update
  • Merge master back into develop

@github-actions github-actions Bot added documentation Improvements or additions to documentation source tests dependencies configuration labels Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d84c772-1ba8-4c83-a8b6-7fad702a5917

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Prepare v2.3.0: optional Mongo driver + MySQL structured logging backend

✨ Enhancement 🐞 Bug fix 📝 Documentation 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add MySQL/PDO structured request logging with example DDL and builder helpers.
• Make MongoDB logging a soft dependency; enforce exclusive Mongo/MySQL backend selection.
• Harden observability and resilience: sanitize exception messages, fix curl quoting, add PSR-16
 bulkhead store.
Diagram

graph TD
  A["Consuming app"] --> B["ClientBuilder"] --> C["LoggingBuilderMethods"]
  C -->|"mongo"| D["MongoDbLogger"] --> F["RequestLogDocumentBuilder"] --> G[("MongoDB")]
  C -->|"mysql"| E["MySqlLogger"] --> F --> H[("MySQL table")]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Allow dual-write (fan-out) structured logging
  • ➕ Supports migration by writing to MongoDB and MySQL simultaneously
  • ➕ No breaking runtime behavior for apps that accidentally call both helper sets
  • ➖ Higher risk of silent double-logging/cost explosions
  • ➖ Harder to reason about schema/versioning across stores
  • ➖ Adds operational coupling and failure-mode complexity (partial writes, retries)
2. Expose a generic StructuredLoggerInterface + backend plugins
  • ➕ Cleaner abstraction boundary; builder only depends on an interface
  • ➕ Backends can live in separate packages to keep core lean
  • ➖ Bigger API and packaging change (likely needs a major version)
  • ➖ More discovery/documentation burden for consumers in the short term

Recommendation: Keep the PR’s current approach: enforce single-backend selection and fail fast on mixing. It minimizes accidental dual-write, keeps the core API simple, and aligns with making Mongo/PDO drivers optional while still providing convenience helpers.

Files changed (42) +2027 / -479

Enhancement (9) +882 / -251
ClientBuilder.phpAdd structured logging exclusivity and optional bulkhead store wiring +36/-27

Add structured logging exclusivity and optional bulkhead store wiring

• Extracts Mongo/MySQL logging convenience methods into a trait and enforces exclusive backend selection per builder (withLogger resets the claim). Extends withBulkhead() to accept an optional BulkheadStoreInterface for persistence customization.

src/Client/ClientBuilder.php

LoggingBuilderMethods.phpAdd Mongo/MySQL logging helper trait for ClientBuilder +101/-0

Add Mongo/MySQL logging helper trait for ClientBuilder

• Adds fluent helper methods for Mongo and MySQL structured logging that delegate to a common structured logger registration and enforce mutual exclusivity.

src/Client/LoggingBuilderMethods.php

MongoDbLogger.phpMake Mongo driver optional and reuse shared document builder +64/-221

Make Mongo driver optional and reuse shared document builder

• Refactors MongoDbLogger to build documents via RequestLogDocumentBuilder and converts configuration validation errors to InvalidConfigurationException. Adds driver/extension availability checks for factory methods and ensures logged_at is BSON-wrapped only when ext-mongodb is present.

src/Logging/MongoDbLogger.php

MySqlLogConfig.phpAdd MySqlLogConfig value object +27/-0

Add MySqlLogConfig value object

• Introduces a readonly config object for DSN-based MySqlLogger setup, validating DSN/table presence, non-negative limits, and schemaVersion.

src/Logging/MySqlLogConfig.php

MySqlLogRowMapper.phpAdd document-to-PDO row mapper for MySQL logger +91/-0

Add document-to-PDO row mapper for MySQL logger

• Adds a mapper that converts structured log documents into a parameter array suitable for PDO inserts, including datetime formatting and JSON context encoding.

src/Logging/MySqlLogRowMapper.php

MySqlLogger.phpAdd PDO-based MySQL/MariaDB structured logger +257/-0

Add PDO-based MySQL/MariaDB structured logger

• Implements a PSR-3 logger that persists structured request logs to SQL via PDO, sharing the RequestLogDocumentBuilder for redaction/limits. Provides factory methods for PDO, DSN, and config, validates safe table names, and fails closed when PDO/pdo_mysql are missing.

src/Logging/MySqlLogger.php

RequestLogDocumentBuilder.phpIntroduce shared structured request-log document builder +232/-0

Introduce shared structured request-log document builder

• Adds a storage-agnostic builder used by MongoDbLogger and MySqlLogger to normalize context, redact sensitive keys, promote common columns, and trim payload bodies.

src/Logging/RequestLogDocumentBuilder.php

Psr16BulkheadStore.phpAdd PSR-16 backed bulkhead concurrency store +62/-0

Add PSR-16 backed bulkhead concurrency store

• Introduces a best-effort PSR-16 bulkhead counter implementation with explicit warning about non-atomic RMW behavior under concurrency.

src/Resilience/Storage/Psr16BulkheadStore.php

PackageVersion.phpAdd reset() with optional composer.json path override for tests +12/-3

Add reset() with optional composer.json path override for tests

• Adds a test hook to reset cached version resolution and optionally point to a temporary composer.json, enabling deterministic unit tests.

src/Support/PackageVersion.php

Bug fix (2) +35 / -11
GuzzleHttpClientAdapter.phpSanitize exception messages before throwing client exceptions +32/-6

Sanitize exception messages before throwing client exceptions

• Introduces LogSanitizer usage to redact sensitive query parameters (and truncate long text) in adapter-thrown exception messages. Applies to sync and async send paths including timeout/connect failures.

src/Adapters/Guzzle/GuzzleHttpClientAdapter.php

CurlExporter.phpFix POSIX single-quote escaping and centralize sensitive header list +3/-5

Fix POSIX single-quote escaping and centralize sensitive header list

• Updates quoting to correct POSIX-safe escaping for embedded single quotes. Switches sensitive header detection to LogSanitizer::SENSITIVE_HEADERS for consistency.

src/Support/CurlExporter.php

Tests (10) +692 / -10
GuzzleHttpClientAdapterTest.phpAdd test for redaction of sensitive query params in exception messages +21/-0

Add test for redaction of sensitive query params in exception messages

• Adds a unit test ensuring access_token query values are redacted in NetworkConnectionException messages while preserving non-sensitive params.

tests/Unit/Adapters/GuzzleHttpClientAdapterTest.php

ClientBuilderMySqlLoggingTest.phpAdd ClientBuilder wiring tests for MySQL logging helpers +111/-0

Add ClientBuilder wiring tests for MySQL logging helpers

• Verifies withMySqlTableLogging(), withMySqlDsnLogging(), and withMySqlLoggingConfig() wire a working client and persist at least one log row (using SQLite for portability).

tests/Unit/Client/ClientBuilderMySqlLoggingTest.php

ClientBuilderStructuredLoggingExclusiveTest.phpAdd tests for exclusive Mongo/MySQL structured backend selection +150/-0

Add tests for exclusive Mongo/MySQL structured backend selection

• Adds coverage that mixing Mongo and MySQL helpers throws InvalidConfigurationException, same-backend reconfiguration is allowed, and withLogger resets the structured-backend claim.

tests/Unit/Client/ClientBuilderStructuredLoggingExclusiveTest.php

MongoDbLoggerTest.phpUpdate MongoDbLogger validation expectations to InvalidConfigurationException +4/-3

Update MongoDbLogger validation expectations to InvalidConfigurationException

• Aligns tests with new InvalidConfigurationException usage when trim limits or schemaVersion are invalid.

tests/Unit/Logging/MongoDbLoggerTest.php

MySqlLoggerTest.phpAdd unit tests for MySqlLogger, config validation, and document builder +289/-0

Add unit tests for MySqlLogger, config validation, and document builder

• Adds extensive tests for row persistence, level coverage, writer failure swallowing, config validation, table-name safety, and RequestLogDocumentBuilder edge cases (payload trimming, redaction, datetime normalization).

tests/Unit/Logging/MySqlLoggerTest.php

ExtendedMiddlewareTest.phpMake rate-limit bypass test deterministic and assert real behavior +10/-3

Make rate-limit bypass test deterministic and assert real behavior

• Removes a tautological assertion and instead verifies bypass does not consume tokens, using refillRatePerSecond=0 and an explicit store for deterministic behavior.

tests/Unit/Middleware/ExtendedMiddlewareTest.php

NewMiddlewareRemainingCoverageTest.phpReplace tautological asserts with concrete header assertions +9/-2

Replace tautological asserts with concrete header assertions

• Captures the request mutated by Authentication middleware and asserts expected empty Authorization/X-Api-Key headers for empty credentials.

tests/Unit/Middleware/NewMiddlewareRemainingCoverageTest.php

Psr16BulkheadStoreTest.phpAdd unit tests for Psr16BulkheadStore behavior +59/-0

Add unit tests for Psr16BulkheadStore behavior

• Covers acquire/release behavior, partition independence, decrementing multiple held slots, and handling cache set failures.

tests/Unit/Resilience/Psr16BulkheadStoreTest.php

CurlExporterTest.phpAdd test for POSIX-safe single-quote escaping in curl output +10/-0

Add test for POSIX-safe single-quote escaping in curl output

• Ensures CurlExporter correctly escapes embedded single quotes in both URL and body payload for shell-safe output.

tests/Unit/Support/CurlExporterTest.php

PackageVersionTest.phpAdd tests for composer version parsing and reset behavior +29/-2

Add tests for composer version parsing and reset behavior

• Adds test coverage for dev fallback when version is missing and for reading an explicit version, using PackageVersion::reset() and temp files to avoid global state leakage.

tests/Unit/Support/PackageVersionTest.php

Documentation (18) +255 / -35
AGENTS.mdDocument optional Mongo/MySQL logging and PSR-16 probe best-effort behavior +3/-2

Document optional Mongo/MySQL logging and PSR-16 probe best-effort behavior

• Adds guidance that MongoDB logging is consumer-declared (mongodb/mongodb + ext-mongodb) and MySQL logging uses PDO (pdo_mysql for MySQL DSNs). Clarifies PSR-16 state store best-effort behavior for tryClaimProbe().

AGENTS.md

CHANGELOG.mdAdd v2.3.0 release notes +21/-0

Add v2.3.0 release notes

• Introduces the 2.3.0 changelog entry covering MySQL logging, optional Mongo dependency, exclusive backend selection, bulkhead store option, doc updates, and curl quoting fix.

CHANGELOG.md

CLAUDE.mdAdd rule: keep structured logging backends optional +1/-0

Add rule: keep structured logging backends optional

• Updates AI guidance to avoid reintroducing hard Composer requirements for Mongo/MySQL logging drivers.

CLAUDE.md

README.mdDocument choose-one structured backend and MySQL logging setup +49/-4

Document choose-one structured backend and MySQL logging setup

• Updates runtime dependency description to make MongoDB optional and introduce PDO requirements for MySQL logging. Adds a choose-one backend section, MySQL examples, and migration notes pointing to UPGRADE-2.3.md.

README.md

UPGRADE-2.3.mdAdd upgrade guide for soft Mongo dependency + backend exclusivity +69/-0

Add upgrade guide for soft Mongo dependency + backend exclusivity

• New upgrade guide instructing Mongo logging consumers to declare mongodb/mongodb + ext-mongodb themselves. Documents exclusive Mongo/MySQL backend selection, MySQL logging prerequisites, and behavior changes around message redaction and curl quoting.

UPGRADE-2.3.md

mongodb-config.mdUpdate Mongo skill for soft dependency and MySQL parallel backend +5/-2

Update Mongo skill for soft dependency and MySQL parallel backend

• Reframes mongodb/mongodb + ext-mongodb as optional (suggested) and describes fail-closed behavior when missing. Adds guidance on choosing Mongo or MySQL helpers (no dual-write).

ai/skills/mongodb-config.md

01-project-overview.mdBump release target to 2.3.0 and mark Mongo as optional/suggested +5/-5

Bump release target to 2.3.0 and mark Mongo as optional/suggested

• Updates architecture docs to reference release target 2.3.0, MongoDB library as suggested, and PHPStan as level max. Keeps tooling/dependency tables consistent with composer.json.

docs/00-architecture/01-project-overview.md

03-tech-stack.mdMark Mongo logging optional and update PHPStan wording +2/-2

Mark Mongo logging optional and update PHPStan wording

• Clarifies mongodb/mongodb + ext-mongodb are optional and only needed when using Mongo logging. Updates PHPStan documentation to level max.

docs/00-architecture/03-tech-stack.md

04-modules-and-domains.mdRemove fabricated CVE placeholder; document coalescing deprecation +5/-3

Remove fabricated CVE placeholder; document coalescing deprecation

• Rewords cache security section to describe JSON hardening without a fake CVE identifier. Updates feature inventory to omit coalescing as “implemented” and marks RequestCoalescingMiddleware as deprecated with rationale.

docs/00-architecture/04-modules-and-domains.md

05-data-flow.mdClarify PHPStan level max and best-effort limits for PSR-16 stores +8/-7

Clarify PHPStan level max and best-effort limits for PSR-16 stores

• Updates PHPStan wording and expands resilience-store limitations (InMemory vs PSR-16, tryClaimProbe best-effort caveat). Rewords deserialization security section to focus on JSON hardening.

docs/00-architecture/05-data-flow.md

business-context-and-goals.mdDescribe optional Mongo driver and PDO-based MySQL logging +1/-1

Describe optional Mongo driver and PDO-based MySQL logging

• Updates business context to state Mongo logging is optional/consumer-declared and MySQL logging is available via PDO without extra Composer packages.

docs/00-architecture/business-context-and-goals.md

basic-concepts.mdBump release target to 2.3.0 and clarify Mongo optionality +4/-4

Bump release target to 2.3.0 and clarify Mongo optionality

• Updates release target references and changes the Mongo logging compatibility note to reflect composer suggest (consumer-declared driver/extension). Also updates PHPStan wording to level max.

docs/01-getting-started/basic-concepts.md

api-reference.mdDocument coalescing deprecation and add MySQL logging reference +23/-1

Document coalescing deprecation and add MySQL logging reference

• Marks withRequestCoalescing() as deprecated with explicit rationale and retention plan. Adds notes on Mongo prerequisites and MySQL/PDO structured logging usage and DDL reference.

docs/02-user-guide/api-reference.md

classes-reference.mdAdd MySQL/Mongo logging helpers and mark coalescing middleware deprecated +3/-1

Add MySQL/Mongo logging helpers and mark coalescing middleware deprecated

• Extends ClientBuilder method list with Mongo and MySQL structured logging helpers. Updates middleware table to label RequestCoalescingMiddleware deprecated under the synchronous pipeline.

docs/02-user-guide/classes-reference.md

client_request_logs.sqlAdd example MySQL/MariaDB table schema for structured request logs +24/-0

Add example MySQL/MariaDB table schema for structured request logs

• Introduces a framework-agnostic CREATE TABLE example for MySqlLogger, including promoted columns and JSON context storage plus useful indexes.

docs/03-examples/sql/client_request_logs.sql

confidence-levels.mdRestore confidence-levels doc with PCRE2 link-check guidance +26/-0

Restore confidence-levels doc with PCRE2 link-check guidance

• Adds a documentation standard for labeling claims (Verified/Observed/Inferred/Assumed). Includes a ripgrep PCRE2 command to reliably detect broken internal Markdown links.

docs/04-development/confidence-levels.md

BACKLOG-POST-2.0.mdMark doc inventory refresh complete and adjust suggested scheduling +2/-2

Mark doc inventory refresh complete and adjust suggested scheduling

• Marks the stale “Features NOT Implemented” refresh task done and removes it from suggested 2.1 candidates. Aligns backlog metadata with updated docs.

docs/05-maintenance/BACKLOG-POST-2.0.md

Psr16StateStore.phpClarify PSR-16 probe-claiming semantics in docblock +4/-1

Clarify PSR-16 probe-claiming semantics in docblock

• Expands warnings to state tryClaimProbe() is best-effort only under PSR-16 and recommends CAS-capable stores for true distributed single-flight semantics.

src/Resilience/Storage/Psr16StateStore.php

Other (3) +163 / -172
composer.jsonMove mongodb/mongodb to suggest; add PDO-related suggestions; keep mongodb in require-dev +5/-2

Move mongodb/mongodb to suggest; add PDO-related suggestions; keep mongodb in require-dev

• Removes mongodb/mongodb from runtime require and adds it to suggest, alongside ext-mongodb and PDO extension suggestions. Adds mongodb/mongodb to require-dev for repository CI/tests.

composer.json

composer.lockUpdate lockfile for mongodb/mongodb as dev dependency +158/-158

Update lockfile for mongodb/mongodb as dev dependency

• Regenerates composer.lock so mongodb/mongodb (and its polyfill) are tracked under packages-dev rather than runtime packages, matching the new soft dependency model.

composer.lock

phpstan-tests-baseline.neonRemove baseline entries for tautological assertTrue warnings +0/-12

Remove baseline entries for tautological assertTrue warnings

• Drops baseline suppressions for assertions that always evaluate true, matching updated tests that now assert meaningful behavior.

phpstan-tests-baseline.neon

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.87879% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.84%. Comparing base (eb63263) to head (07b3fa2).

Files with missing lines Patch % Lines
src/Logging/RequestLogDocumentBuilder.php 95.09% 5 Missing ⚠️
src/Logging/MongoDbLogger.php 95.23% 1 Missing ⚠️
src/Logging/MySqlLogRowMapper.php 97.50% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master      #44      +/-   ##
============================================
+ Coverage     98.82%   98.84%   +0.01%     
- Complexity      949     1023      +74     
============================================
  Files            70       76       +6     
  Lines          2219     2430     +211     
============================================
+ Hits           2193     2402     +209     
- Misses           26       28       +2     
Flag Coverage Δ
unittests 98.84% <97.87%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@soulevilx
soulevilx merged commit 13fe6ea into master Aug 3, 2026
31 checks passed
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Hard MongoDB\Collection typehint 📘 Rule violation ☼ Reliability
Description
ClientBuilder always loads LoggingBuilderMethods, which imports and typehints
MongoDB\Collection; this makes mongodb/mongodb effectively required at runtime even though it
was moved to composer.json:suggest. Consumers without the Mongo driver can hit a fatal
class-resolution error just by loading the builder, breaking the “optional backend” requirement.
Code

src/Client/LoggingBuilderMethods.php[11]

+use MongoDB\Collection;
Evidence
Rule 4 requires MongoDB logging dependencies remain optional (no hard requirement). While
composer.json now only suggests mongodb/mongodb, LoggingBuilderMethods imports and uses
MongoDB\Collection in a public method signature, and ClientBuilder always pulls this trait in,
making the MongoDB library effectively required when loading the builder.

AGENTS.md: MongoDB and MySQL Structured Logging Backends Must Remain Optional (No Hard Composer Requires)
composer.json[38-44]
src/Client/LoggingBuilderMethods.php[7-33]
src/Client/ClientBuilder.php[85-90]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`mongodb/mongodb` was moved to `composer.json:suggest`, but `src/Client/LoggingBuilderMethods.php` imports and typehints `MongoDB\Collection`. Because `ClientBuilder` unconditionally uses this trait, the package can require the MongoDB classes to exist at load time, defeating the goal of keeping Mongo logging optional.

## Issue Context
Compliance requires MongoDB/MySQL structured logging backends remain optional and not introduce hard dependency requirements for consumers who do not use them.

## Fix Focus Areas
- src/Client/LoggingBuilderMethods.php[7-33]
- src/Client/ClientBuilder.php[85-90]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Bulkhead counter never expires 🐞 Bug ☼ Reliability
Description
Psr16BulkheadStore::tryAcquire() increments the active counter via PSR-16 set() without any TTL,
so a process crash/kill between acquire and the finally-based release can leave a stale count that
blocks future requests for that partition. This is specific to the new PSR-16 bulkhead store because
the value represents in-flight work but is persisted indefinitely.
Code

src/Resilience/Storage/Psr16BulkheadStore.php[R35-36]

+        return $this->cache->set($cacheKey, $current + 1);
+    }
Evidence
The bulkhead middleware only releases in a finally block, which is not guaranteed to run on
process termination, and the new PSR-16 store persists the in-flight counter without any expiration,
allowing stale counts to remain.

src/Resilience/Storage/Psr16BulkheadStore.php[27-36]
src/Middleware/BulkheadMiddleware.php[24-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Psr16BulkheadStore` stores an “active in-flight requests” counter in PSR-16 without expiry. If the PHP worker terminates before `BulkheadMiddleware` reaches its `finally` block, the counter can remain non-zero indefinitely and the partition will be rejected until manual cache cleanup.

### Issue Context
This store is meant to represent *in-flight* concurrency, unlike circuit/rate-limit state which can safely persist. A TTL/lease mechanism is needed to self-heal from unclean shutdowns.

### Fix Focus Areas
- src/Resilience/Storage/Psr16BulkheadStore.php[19-61]
- src/Middleware/BulkheadMiddleware.php[24-37]

### Implementation notes
- Add a configurable TTL/lease duration (e.g., constructor parameter like `$leaseSeconds`, default conservative value).
- Pass that TTL to `CacheInterface::set()` in `tryAcquire()` and also when decrementing in `release()` (refresh/extend as needed).
- Document the tradeoff: TTL must exceed maximum expected request duration; otherwise long requests could expire and undercount.
- Add/adjust unit tests to cover TTL usage (at least asserting `set()` is called with TTL via a spy cache).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Prepare per log entry 🐞 Bug ➹ Performance
Description
MySqlLogger::createPdoWriter() prepares the same INSERT statement on every log write, and
LoggingMiddleware emits multiple log entries per request, causing repeated prepare() overhead on
hot paths. This can significantly increase latency and DB load under high request volume.
Code

src/Logging/MySqlLogger.php[R211-214]

+        return static function (array $document) use ($pdo, $sql): void {
+            $statement = $pdo->prepare($sql);
+            $statement->execute(MySqlLogRowMapper::toRow($document));
+        };
Evidence
The writer closure prepares inside the per-document function, and the logging middleware performs
multiple log calls during a single request/response cycle, multiplying the number of prepares.

src/Logging/MySqlLogger.php[207-215]
src/Middleware/LoggingMiddleware.php[51-75]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The default PDO writer created by `MySqlLogger::createPdoWriter()` calls `$pdo->prepare($sql)` for every log document. Since the middleware logs at least “Sending request” and “Received response” per HTTP call, this results in multiple prepares per request.

### Issue Context
The writer closure is created once per logger instance and captures the PDO connection and SQL string, so it can also capture a prepared `PDOStatement` and reuse it.

### Fix Focus Areas
- src/Logging/MySqlLogger.php[204-215]
- src/Middleware/LoggingMiddleware.php[51-75]

### Implementation notes
- Prepare once when building the writer:
 - `$statement = $pdo->prepare($sql);` outside the returned closure.
 - If `$statement === false`, throw `InvalidConfigurationException` (fail fast on misconfigured schema/SQL).
 - In the closure, only call `$statement->execute(MySqlLogRowMapper::toRow($document));`.
- Consider whether you need to re-prepare on failure (optional), but the minimum improvement is statement reuse.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

use JOOservices\Client\Logging\MongoDbLogger;
use JOOservices\Client\Logging\MySqlLogConfig;
use JOOservices\Client\Logging\MySqlLogger;
use MongoDB\Collection;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Hard mongodb\collection typehint 📘 Rule violation ☼ Reliability

ClientBuilder always loads LoggingBuilderMethods, which imports and typehints
MongoDB\Collection; this makes mongodb/mongodb effectively required at runtime even though it
was moved to composer.json:suggest. Consumers without the Mongo driver can hit a fatal
class-resolution error just by loading the builder, breaking the “optional backend” requirement.
Agent Prompt
## Issue description
`mongodb/mongodb` was moved to `composer.json:suggest`, but `src/Client/LoggingBuilderMethods.php` imports and typehints `MongoDB\Collection`. Because `ClientBuilder` unconditionally uses this trait, the package can require the MongoDB classes to exist at load time, defeating the goal of keeping Mongo logging optional.

## Issue Context
Compliance requires MongoDB/MySQL structured logging backends remain optional and not introduce hard dependency requirements for consumers who do not use them.

## Fix Focus Areas
- src/Client/LoggingBuilderMethods.php[7-33]
- src/Client/ClientBuilder.php[85-90]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +35 to +36
return $this->cache->set($cacheKey, $current + 1);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Bulkhead counter never expires 🐞 Bug ☼ Reliability

Psr16BulkheadStore::tryAcquire() increments the active counter via PSR-16 set() without any TTL,
so a process crash/kill between acquire and the finally-based release can leave a stale count that
blocks future requests for that partition. This is specific to the new PSR-16 bulkhead store because
the value represents in-flight work but is persisted indefinitely.
Agent Prompt
### Issue description
`Psr16BulkheadStore` stores an “active in-flight requests” counter in PSR-16 without expiry. If the PHP worker terminates before `BulkheadMiddleware` reaches its `finally` block, the counter can remain non-zero indefinitely and the partition will be rejected until manual cache cleanup.

### Issue Context
This store is meant to represent *in-flight* concurrency, unlike circuit/rate-limit state which can safely persist. A TTL/lease mechanism is needed to self-heal from unclean shutdowns.

### Fix Focus Areas
- src/Resilience/Storage/Psr16BulkheadStore.php[19-61]
- src/Middleware/BulkheadMiddleware.php[24-37]

### Implementation notes
- Add a configurable TTL/lease duration (e.g., constructor parameter like `$leaseSeconds`, default conservative value).
- Pass that TTL to `CacheInterface::set()` in `tryAcquire()` and also when decrementing in `release()` (refresh/extend as needed).
- Document the tradeoff: TTL must exceed maximum expected request duration; otherwise long requests could expire and undercount.
- Add/adjust unit tests to cover TTL usage (at least asserting `set()` is called with TTL via a spy cache).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +211 to +214
return static function (array $document) use ($pdo, $sql): void {
$statement = $pdo->prepare($sql);
$statement->execute(MySqlLogRowMapper::toRow($document));
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Prepare per log entry 🐞 Bug ➹ Performance

MySqlLogger::createPdoWriter() prepares the same INSERT statement on every log write, and
LoggingMiddleware emits multiple log entries per request, causing repeated prepare() overhead on
hot paths. This can significantly increase latency and DB load under high request volume.
Agent Prompt
### Issue description
The default PDO writer created by `MySqlLogger::createPdoWriter()` calls `$pdo->prepare($sql)` for every log document. Since the middleware logs at least “Sending request” and “Received response” per HTTP call, this results in multiple prepares per request.

### Issue Context
The writer closure is created once per logger instance and captures the PDO connection and SQL string, so it can also capture a prepared `PDOStatement` and reuse it.

### Fix Focus Areas
- src/Logging/MySqlLogger.php[204-215]
- src/Middleware/LoggingMiddleware.php[51-75]

### Implementation notes
- Prepare once when building the writer:
  - `$statement = $pdo->prepare($sql);` outside the returned closure.
  - If `$statement === false`, throw `InvalidConfigurationException` (fail fast on misconfigured schema/SQL).
  - In the closure, only call `$statement->execute(MySqlLogRowMapper::toRow($document));`.
- Consider whether you need to re-prepare on failure (optional), but the minimum improvement is statement reuse.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants