DanielLeens commented on PR #11264:
URL: https://github.com/apache/seatunnel/pull/11264#issuecomment-5340779674

   Self-review note: I am the author of this PR, so GitHub will not let me 
submit a formal Approve/Request-changes review here — posting this as a plain 
comment instead, with an explicit merge recommendation in prose at the end.
   
   This is a full from-scratch pass on the current head `5718b51d8` (unchanged 
since my last complete review on 2026-08-16, three days ago — `git diff` of 
that head against today's head is empty). Rather than replaying the previous 
write-up, I independently re-derived every claim below straight from the 
current source (`DateTimeUtils.java`, `JsonToRowConverters.java`, 
`JsonDeserializationSchema.java`) and from live CI logs pulled just now, and I 
flag explicitly which conclusions are carried forward unchanged vs. what 
changed since the last round.
   
   # What Problem Does This PR Solve?
   
   - **User pain point.** `JsonToRowConverters` resolves and caches exactly one 
`DateTimeFormatter` per field name (`fieldFormatterMap`), based on the *first* 
value seen for that field. If the first row for a `TIMESTAMP` field is 
second-precision (`2022-09-24T22:45:00`, length 19), 
`DateTimeUtils.matchDateTimeFormatter` returns the strict length-19 pattern 
`yyyy-MM-dd'T'HH:mm:ss` with no fractional component. A later row for the same 
field carrying a fractional second (`2022-09-24T22:45:00.123`) then fails 
`dateTimeFormatter.parse(...)` with `DateTimeParseException`. Depending on 
`ignoreParseErrors`, this either rejects the whole row 
(`CommonError.jsonOperationError`) or silently nulls the field. This is exactly 
the `FakeSource` → JSON round-trip failure behind the flaky `VictoriaMetricsIT` 
CI failure this PR was opened to fix.
   - **Fix approach.** New private helper `parseDateTimeWithFormatterRefresh` 
(`JsonToRowConverters.java:316-345`): try the cached formatter first; on 
`DateTimeParseException`, re-run `DateTimeUtils.matchDateTimeFormatter` against 
the *current* text, overwrite the field's cache entry, and retry once. Plus one 
regression test that deserializes both precisions through the same 
`JsonDeserializationSchema` instance.
   - **One-sentence summary.** Lets a per-field cached JSON timestamp formatter 
recover instead of failing when the same field mixes second-only and 
fractional-second text across rows.
   
   # 1. Code Change Review
   
   ## 1.1 Core Logic Analysis
   
   **Files touched:** 
`seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/JsonToRowConverters.java`,
 
`seatunnel-formats/seatunnel-format-json/src/test/java/org/apache/seatunnel/format/json/JsonRowDataSerDeSchemaTest.java`
 — 2 files, +49/-1, verified against `dev` via `git diff` in a fresh worktree.
   
   Before (`JsonToRowConverters.java:315`, pre-PR):
   ```java
   TemporalAccessor parsedTimestamp = dateTimeFormatter.parse(datetimeStr);
   ```
   
   After (`JsonToRowConverters.java:316-345`):
   ```java
   TemporalAccessor parsedTimestamp =
           parseDateTimeWithFormatterRefresh(datetimeStr, fieldName, 
dateTimeFormatter);
   ...
   private TemporalAccessor parseDateTimeWithFormatterRefresh(
           String datetimeStr, String fieldName, DateTimeFormatter 
dateTimeFormatter) {
       try {
           return dateTimeFormatter.parse(datetimeStr);
       } catch (DateTimeParseException parseException) {
           if (StringUtils.isBlank(fieldName)) {
               throw parseException;
           }
           DateTimeFormatter refreshedFormatter = 
DateTimeUtils.matchDateTimeFormatter(datetimeStr);
           if (refreshedFormatter == null) {
               throw CommonError.formatDateTimeError(datetimeStr, fieldName);
           }
           fieldFormatterMap.put(fieldName, refreshedFormatter);
           return refreshedFormatter.parse(datetimeStr);
       }
   }
   ```
   The cache-resolution block above it (`:296-314`: lookup, first-match via 
`matchDateTimeFormatter`, cache write, null check) is untouched, so the helper 
always receives a non-null formatter.
   
   **Directly answering the "per-record vs per-batch refresh" question, since 
that is the crux of this fix.** The refresh is neither: it is *per-field, 
convergent, and happens at most once for the lifetime of the deserializer 
instance* for the scenario this PR targets. The reason is 
`DateTimeUtils.matchDateTimeFormatter` (`DateTimeUtils.java:224-269`), which 
dispatches purely on **string length**:
   - length `== 19` → strict formatters with **no** optional fraction 
(`yyyy-MM-dd'T'HH:mm:ss`, no `.SSS`).
   - length `> 19` → a *different* map whose ISO entry is 
`DateTimeFormatter.ISO_LOCAL_DATE_TIME`, and whose space/slash/dot siblings all 
embed `DateTimeFormatter.ISO_LOCAL_TIME` (`DateTimeUtils.java:128-143`). 
`ISO_LOCAL_TIME`'s fractional-second component is optional and accepts 1–9 
digits, so this single cached formatter parses **both** second-only and 
fractional text once the field's row order has produced any row longer than 19 
characters.
   
   I hand-traced 5 rows through one field/cache with `ignoreParseErrors=false`:
   
   | # | Input | len | Cache in | dev (before) | This PR | Cache out |
   |---|---|---|---|---|---|---|
   | 1 | `...22:45:00` | 19 | empty | OK | OK (unchanged) | strict-19 |
   | 2 | `...22:45:00.123` | 23 | strict-19 | row rejected | refresh → 
`ISO_LOCAL_DATE_TIME` → OK | ISO |
   | 3 | `...22:45:00.123456` | 26 | ISO | row rejected (dev) | parses 
directly, **no refresh** | ISO |
   | 4 | `...22:45:00` (back to seconds) | 19 | ISO | OK | ISO parses it too, 
**no refresh** | ISO |
   | 5 | `...22:45:00.1` | 21 | ISO | row rejected | ISO → nanos `100_000_000`, 
OK | ISO |
   
   Rows 3–5 confirm the "boundary values" question directly asked in this 
review's scope (0/3/6/9 fractional digits, and switching back across the second 
boundary): once the cache holds the `> 19` bucket formatter, every subsequent 
precision — including a return to whole seconds — takes the plain cache-hit 
path with **zero** further exceptions or re-matches. There is no per-record 
thrashing on alternating precision; the only cost is the single exception + 
single `matchDateTimeFormatter` call on the row where the field's text first 
exceeds 19 characters.
   
   **Key findings**
   1. The normal deserialization path (`JsonDeserializationSchema.deserialize` 
→ `JsonToRowConverters.convertToLocalDateTime`) reaches this change on every 
`TIMESTAMP` field parse; it is not a recovery-only or exceptional-branch fix.
   2. The fix is precise for its stated scope (mixed second/fractional 
precision on the same JSON `\d{4}-\d{2}-\d{2}[T 
]\d{2}:\d{2}:\d{2}(\.\d+)?`-shaped field) and is *stronger* than the PR 
description implies, because the target formatter is precision-agnostic, not 
just "the second formatter it happened to see."
   3. Traced independently: array elements (`createArrayConverter`, 
`JsonToRowConverters.java:461`) and map values (`createMapConverter`, 
`:490-492`) both inherit a non-blank parent field name, and the top-level call 
(`JsonDeserializationSchema.java:171,185`) passes a non-blank `fieldNames[i]` — 
so the `StringUtils.isBlank(fieldName)` branch in the new helper is unreachable 
from this module's deserialization path. It does not exclude nested 
`ARRAY<TIMESTAMP>`/`MAP` values from the fix, contrary to what two earlier 
review rounds (mine on 2026-08-13, and @SEZ9's) concluded.
   4. Confirmed gap: the `> 19` length bucket 
(`YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP`, `DateTimeUtils.java:128-143`) has no 
`\d{1,2}` (single-digit month/day) pattern, unlike the length-17/18 bucket. A 
field whose first row is `2022-9-24 22:45:00` (18 chars, matches the `\d{1,2}` 
bucket) and whose later row is `2022-9-24 22:45:00.123` (22 chars, `> 19` 
bucket) still fails, because none of the `> 19` patterns match non-zero-padded 
month/day. This is a real, still-open gap in the fix's own stated scope, and it 
lives in shared `DateTimeUtils`, not in this diff.
   5. Write path is untouched and was never affected: `RowToJsonConverters`'s 
`TIMESTAMP` serializer is a stateless `ISO_LOCAL_DATE_TIME.format(...)` call 
with no cache, so it already emitted exactly the precision present in each 
value. The bug was reader-only, and a SeaTunnel JSON writer → JSON reader round 
trip could previously produce data SeaTunnel itself could not read back once 
precision varied within a field; this PR closes that self-inconsistency.
   
   ## 1.2 Compatibility Impact
   
   **Fully compatible.** No API, SPI, `Option`, config key, default value, 
protocol, or checkpoint/serialized-state change. `JsonToRowConverters` gains no 
new field and no `serialVersionUID` impact.
   
   No previously-produced *value* changes: 
`DateTimeFormatter.parse(CharSequence)` performs a full-text match and rejects 
trailing content, so on `dev` the old cached formatter either matched exactly 
(value unchanged by this PR) or threw — there is no code path where the old 
behavior silently produced a wrong/truncated `LocalDateTime`. The only two 
behavior deltas are both strict improvements: rows that previously threw and 
rejected the whole record now succeed, and rows that previously produced a 
silent `null` under `ignoreParseErrors=true` now produce a real value. That 
second delta is worth one sentence in the PR description, since any downstream 
logic special-casing those nulls (a `WHERE ts IS NULL` filter, a null-count 
alert) will observe a behavior change, even though it is a correction of prior 
silent data loss rather than a regression.
   
   ## 1.3 Performance / Side-Effect Analysis
   
   - **Stable-precision fields (the common case): effectively zero cost.** One 
`try` around the same single `parse()` call; a JIT'd try with no throw is free.
   - **Mixed-precision fields (the target case): one-time cost per field, not 
per record.** Exactly one `DateTimeParseException` construction plus one 
`matchDateTimeFormatter` call, on the single row where a field's text first 
exceeds 19 characters. Every row after that — of any precision, including a 
return to whole seconds — takes the cache-hit path, per the worked table above. 
This directly answers the "over-eager cache invalidation on the hot path" 
concern raised for this review: there is no per-record invalidation loop; the 
cache converges and stays converged.
   - **Steady-state new cost:** rows that are genuinely malformed (fail for a 
reason unrelated to precision) now pay one extra regex scan plus one extra 
failed parse before the exception escapes, since the retry at 
`JsonToRowConverters.java:343` is unguarded (see Issue 2 below). Bounded, and 
only on data that was already failing on `dev`.
   - **Memory:** unchanged — `fieldFormatterMap` is bounded by distinct 
field-name count; the refresh overwrites an entry, it never grows the map.
   - **Concurrency:** pre-existing, not meaningfully widened. 
`fieldFormatterMap` is a plain `HashMap` with no synchronization, and this PR 
adds a second unguarded write site alongside the existing first-match write. 
`JsonToRowConverters` is owned one-per-`JsonDeserializationSchema`, 
one-per-reader-instance, so this is safe under the standard 
single-threaded-reader usage pattern; I did not exhaustively re-audit every 
connector for a shared-instance-across-threads usage.
   
   ## 1.4 Error Handling and Logging
   
   - The catch is correctly narrowed to `DateTimeParseException`, not a broader 
`RuntimeException`/`Exception`, so unrelated failures (e.g. from 
`TemporalQueries`) are not swallowed into a spurious rematch.
   - The null-match branch (`:339-341`) correctly throws the project-standard 
`CommonError.formatDateTimeError(datetimeStr, fieldName)`.
   - No logging is added on refresh. A field silently switching its resolved 
format mid-stream is the kind of event worth a one-time `log.debug`, naming the 
field and both patterns — see Issue 5.
   
   **Issues found** (numbered once for this code version; all carried forward 
unchanged from findings first raised on 2026-08-13/2026-08-16 and re-verified 
independently against the current head today — none are newly introduced by 
this diff, and none are blocking):
   
   **Issue 1: Mixed precision still fails for non-zero-padded (single-digit) 
month/day shapes, because the `> 19` length bucket has no `\d{1,2}` pattern.**
   - Location: 
`seatunnel-common/src/main/java/org/apache/seatunnel/common/utils/DateTimeUtils.java:128-143`
 (the `> 19` bucket), exercised via `JsonToRowConverters.java:337-341`.
   - Problem: verified by reading the static formatter tables — the 
length-17/18 bucket has `\d{1,2}-\d{1,2}` patterns, but every pattern in the `> 
19` bucket requires zero-padded two-digit month/day. A field whose values go 
from `2022-9-24 22:45:00` (18 chars) to `2022-9-24 22:45:00.123` (22 chars) 
hits `matchDateTimeFormatter` returning `null` on the second row, and the 
helper throws `CommonError.formatDateTimeError`.
   - Potential risk: for hand-written or some database-exported JSON using 
non-zero-padded dates, the fix's own stated scope ("mixed precision") is only 
half-covered, and the failure message doesn't hint that it previously worked at 
a different precision.
   - Best improvement: either extend `YYYY_MM_DD_HH_MM_SS_M19_FORMATTER_MAP` 
with a `\d{1,2}` variant (and its slash counterpart), backed by a 
`DateTimeUtilsTest` case, or explicitly document the supported shape set in the 
PR description if this is intentionally out of scope.
   - Severity: Medium
   - Raised by another reviewer: No (new observation from re-deriving the 
formatter tables byte-for-byte; earlier rounds, including @SEZ9's, did not 
identify this specific gap).
   
   **Issue 2: The retry parse (`JsonToRowConverters.java:343`) is unguarded and 
can leak a raw `DateTimeParseException` instead of `CommonError`.**
   - Location: `JsonToRowConverters.java:343`.
   - Problem: `matchDateTimeFormatter` matches by shape, not semantic validity, 
so a structurally valid but nonsensical value (e.g. `2022-13-45T25:99:00`) 
returns a non-null formatter whose `.parse()` still throws — outside any catch. 
This is not a regression: the identical input already escapes as a raw 
`DateTimeParseException` from the same call site on `dev` today, and both 
exception types are unchecked `RuntimeException`s, so `ignoreParseErrors` 
handling is unaffected. The only real delta is one wasted regex scan and one 
wasted failed parse on rows that were already failing.
   - Potential risk: Low — inconsistent exception type vs. the `CommonError` 
convention used three lines above, but no functional regression.
   - Best improvement: wrap the retry in try/catch and throw 
`CommonError.formatDateTimeError(datetimeStr, fieldName)` on failure, matching 
the null branch immediately above it.
   - Severity: Low
   - Raised by another reviewer: Yes (@SEZ9's Issue 4, rated Medium there — I 
am rating it Low here because, per the trace above, the exception type reaching 
the caller on this exact input is unchanged from `dev`, so this is a missed 
cleanup, not a new defect).
   
   **Issue 3: `StringUtils.isBlank(fieldName)` guard is dead code for this 
module's deserialization path, and its presence has already caused two 
independent reviewers to draw the wrong conclusion about nested value 
coverage.**
   - Location: `JsonToRowConverters.java:332-335`.
   - Problem: per the field-name propagation trace in 1.1 
(`createArrayConverter`/`createMapConverter`/`createRowConverter`), no call 
path from `JsonDeserializationSchema` reaches `convertToLocalDateTime` with a 
blank field name. The guard can only fire in a hypothetical caller not present 
in this module today.
   - Potential risk: none functionally; the risk is purely readability — this 
guard is precisely what led me (2026-08-13) and @SEZ9 to independently 
conclude, incorrectly, that array/map timestamp elements were excluded from the 
fix.
   - Best improvement: drop the guard and let the rematch run unconditionally, 
guarding only the `fieldFormatterMap.put(...)` cache write with 
`StringUtils.isNotBlank(fieldName)`; or keep it with a one-line comment stating 
it is defensive-only and that array/map elements inherit a non-blank parent 
name.
   - Severity: Low
   - Raised by another reviewer: Yes (@SEZ9's Issue 2/7) — on the premise that 
it disables the fix for nested values, which I traced and found to be 
incorrect; the residual issue is readability only.
   
   **Issue 4: The same cache-once-per-field pattern (with no refresh) exists in 
`DebeziumRowConverter` and `CsvDeserializationSchema`, so CDC/CSV producers of 
mixed-precision timestamps see no improvement from this PR.**
   - Location: 
`seatunnel-formats/seatunnel-format-json/src/main/java/org/apache/seatunnel/format/json/debezium/DebeziumRowConverter.java:177-192`;
 
`seatunnel-formats/seatunnel-format-csv/src/main/java/org/apache/seatunnel/format/csv/CsvDeserializationSchema.java:345-369`.
 Verified `DebeziumRowConverter.java:176-187` directly: it does 
`fieldFormatterMap.get(fieldName)` → `DateUtils.matchDateFormatter(...)` on 
miss → cache-and-parse, with no retry-on-mismatch.
   - Potential risk: CDC is arguably the highest-impact source of same-field 
precision drift in practice, and this sibling path will keep failing the same 
way `JsonToRowConverters` did before this PR.
   - Best improvement: keep this PR scoped as-is; open a follow-up issue 
referencing it so the fix pattern is applied consistently, rather than widening 
this diff.
   - Severity: Low (explicitly a follow-up, not a change request against this 
PR)
   - Raised by another reviewer: No
   
   **Issue 5: No logging on formatter refresh.**
   - Location: `JsonToRowConverters.java:316-345` (the new helper has no logger 
call).
   - Problem/risk: a field silently switching its effective parse format 
mid-stream is invisible to an operator; a single `log.debug` naming the field 
and both patterns at the refresh point would make Issue 1-class gaps and any 
future order-dependent-strictness question diagnosable from logs instead of 
requiring source-level tracing (as this review needed).
   - Best improvement: add one `log.debug`, not `info`, and not per-row (it 
already only fires once per field for the common case, per 1.1).
   - Severity: Low
   - Raised by another reviewer: No
   
   # 2. Code Quality Assessment
   
   ## 2.1 Coding Standards
   
   Google AOSP formatting respected (Spotless passed in the fork's CI). No 
wildcard imports; the new `java.time.format.DateTimeParseException` import is 
correctly grouped. No new files, so no ASF header question. The helper is 
private, minimal, and adjacent to its only caller.
   
   One documentation gap: the Javadoc on `parseDateTimeWithFormatterRefresh` 
explains the trigger but omits `@param`/`@return`/`@throws`, and — more 
importantly for a method that mutates instance state — does not document that 
it writes to `fieldFormatterMap` as a side effect. Given this method's name 
reads as a pure parse, that mutation should be called out. Medium per the 
project's documentation-completeness standard for a new non-trivial method with 
a state side effect (folded into Issue 5's remediation scope above rather than 
numbered separately, since the fix is the same one-line Javadoc edit).
   
   ## 2.2 Test Coverage and Test Stability
   
   **Coverage.** `testTimestampFieldSupportsMixedPrecisionAcrossRows` 
(`JsonRowDataSerDeSchemaTest.java:771-792`, verified via `git diff`) reuses a 
single `JsonDeserializationSchema` instance across two `deserialize()` calls — 
second-precision then fractional — and asserts both results including the 
nanosecond value. This is a genuine cross-row cache-refresh regression test (it 
fails on `dev`, passes with the fix), not two independent single-precision 
checks in disguise. What it does **not** cover, all non-blocking: a third 
precision value (e.g. `.123456`) in the same sequence to pin the "converged 
formatter is precision-agnostic" property the whole fix rests on; the 
return-to-seconds direction (row 4 in the table above); the space-separated 
variant, which converges on a different formatter object than the `T`-separated 
one under test; and an array/map element case, which — per the field-name trace 
in 1.1 — would pass today and would lock in that coverage against a future reg
 ression.
   
   **Stability rating: Stable.** Per Section 5.10.2 of the review checklist: 
the test is a pure in-memory byte-array-to-row assertion with no 
`Thread.sleep`, no clock/timezone dependency (`LocalDateTime`, no zone 
conversion involved), no network, no Testcontainers, no filesystem access, no 
shared mutable/static state (a fresh schema instance is constructed inside the 
test method), and no dependency on JUnit execution order. I confirmed this on 
live CI logs pulled just now, not just by reading the test: on the current 
head's final green fork run, `unit-test (11, ubuntu-latest)` reports `Tests 
run: 20, Failures: 0, Errors: 0, Skipped: 0` for `JsonRowDataSerDeSchemaTest`, 
and the Windows job shows the identical count — so the new test is 
deterministic across both platforms in this run.
   
   ## 2.3 Documentation Updates
   
   None required. No new `Option`, no default-value change, no documented 
contract change. I checked the connector docs that document explicit 
`datetime_format` options (file-based connectors, Maxcompute, GoogleBigtable 
sinks) — none of them describe this format's implicit auto-detection, so none 
goes stale. Worth a follow-up note (non-blocking): the auto-detection contract 
in `DateTimeUtils.matchDateTimeFormatter` — which shapes/precisions are 
accepted — isn't documented anywhere, which is part of why Issue 1's gap wasn't 
obvious without reading the regex tables directly.
   
   # 3. Architectural Soundness
   
   ## 3.1 Elegance of the Solution
   
   **Precise, minimal fix — not the most idiomatic one, but appropriately 
scoped.** `JsonToRowConverters.java` already has the idiomatic pattern for 
exactly this problem, for the `TIME` type: `TIME_FORMAT` is built with 
`DateTimeFormatterBuilder().appendPattern("HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND,
 0, 9, true)`, a single stateless formatter that accepts every precision 
natively with no cache and no refresh logic, which is why `convertToLocalTime` 
has never needed this fix. The `TIMESTAMP` path could in principle follow the 
same model by adding an optional fraction to the length-19 bucket formatters in 
`DateTimeUtils`. I am not asking for that here — `DateTimeUtils`'s static 
tables are shared well beyond this module, and turning a 3-line targeted fix 
into a cross-module change on a hot path is a larger and riskier PR than the 
CI-unblocking scope this one has. The direction is worth recording for whoever 
next touches `DateTimeUtils`, though (tracked via Issue 1/Iss
 ue 4 as follow-up scope).
   
   ## 3.2 Maintainability
   
   Good — the helper is 17 lines, single-purpose, and leaves the pre-existing 
cache-resolution block untouched, so the diff is easy to review and easy to 
revert. The two maintainability warts are both one-line fixes: the dead 
blank-name guard (Issue 3) and the undocumented cache-mutation side effect 
(2.1/Issue 5).
   
   ## 3.3 Extensibility
   
   Neutral — the helper is private and specific to `convertToLocalDateTime`; it 
does not generalize to `convertToLocalDate` (which has the identical 
cache-then-strict-parse shape and the identical untested same-field 
format-drift gap) or to the sibling 
`DebeziumRowConverter`/`CsvDeserializationSchema` converters (Issue 4). 
Acceptable for a targeted bug fix; a shared utility would be the right shape 
for a follow-up that fixes all of them consistently.
   
   ## 3.4 Historical-Version Compatibility
   
   No concern. Nothing in this diff touches checkpoint/savepoint state, 
serialized formats, SPI contracts, `Option` definitions, or job-config parsing. 
A job checkpointed on an older build and restored onto a build carrying this 
change resumes identically — the only difference is that timestamp rows which 
previously failed now parse successfully. Reverting this commit restores the 
old (buggy) behavior with no state migration required in either direction.
   
   # 4. Issue Summary
   
   | # | Issue | Location | Severity |
   |---|---|---|---|
   | 1 | Mixed precision still fails for non-zero-padded month/day shapes (`> 
19` bucket has no `\d{1,2}` pattern) | `DateTimeUtils.java:128-143` via 
`JsonToRowConverters.java:337-341` | Medium |
   | 2 | Retry parse is unguarded and can leak a raw `DateTimeParseException`; 
not a regression vs. `dev`, just a missed cleanup | 
`JsonToRowConverters.java:343` | Low |
   | 3 | `StringUtils.isBlank(fieldName)` guard is dead code for this path and 
has misled two reviewers about nested-value coverage | 
`JsonToRowConverters.java:332-335` | Low |
   | 4 | Same cache-once-per-field bug remains in `DebeziumRowConverter` and 
`CsvDeserializationSchema` (follow-up, not a change request here) | 
`DebeziumRowConverter.java:177-192`, `CsvDeserializationSchema.java:345-369` | 
Low |
   | 5 | No logging on formatter refresh; helper's Javadoc omits the 
`fieldFormatterMap` mutation side effect | `JsonToRowConverters.java:316-345` | 
Low |
   
   # 5. Merge Recommendation
   
   ### Conclusion: Ready to merge after fixes are optional — no source-side 
blocker remains; the only real gates are process gates.
   
   **1. Blockers — none, source-side.** Issue 1 is the only Medium, and it is a 
pre-existing limitation inherited from `DateTimeUtils`'s shared pattern table 
rather than a defect this diff introduces — it narrows the fix's reach 
(non-zero-padded dates) without breaking anything that works today. Nothing 
here needs to be fixed before merge.
   
   **2. Recommended fixes — non-blocking, in priority order:**
   - Extend the regression test with a third precision value and the 
return-to-seconds direction, to pin the "converged formatter is 
precision-agnostic" property the whole design relies on (Issue 2.2 gap).
   - Guard the retry parse and throw `CommonError.formatDateTimeError` for 
consistency (Issue 2).
   - Drop or comment the unreachable blank-name guard (Issue 3).
   - Document the `fieldFormatterMap` mutation side effect in the helper's 
Javadoc, and consider one `log.debug` at the refresh point (Issue 5).
   - Open a follow-up issue for Issue 1 (single-digit month/day gap) and Issue 
4 (`DebeziumRowConverter`/`CsvDeserializationSchema` share the same unfixed 
pattern).
   
   **3. Process gates — what is actually blocking merge right now:**
   - **CI has recovered since my last round.** As of my previous comment 
(2026-08-16) the fork run showed 2 real failures: the recurring Hazelcast `Node 
failed to start!` flake in `unit-test (11, windows-latest)` 
(`JobStateCleanupDelayTest`), and a Couchbase-container `Connection reset` 
flake in `all-connectors-it-1`. I re-checked just now: the same fork run 
(`31792627420`) was rerun to attempt 3 and completed on 2026-08-18 with **all 
real jobs green**, including both previously-failing ones. I independently 
pulled the `unit-test (11, ubuntu-latest)` job log for this exact head and 
confirmed `JsonRowDataSerDeSchemaTest`: `Tests run: 20, Failures: 0, Errors: 
0`. So the CI state I previously described as failing has changed to passing, 
and the last remaining blocker from my prior rounds is now cleared.
   - **The PR is still marked draft** (`isDraft: true`) and cannot merge until 
it's marked ready for review.
   - **`reviewDecision: REVIEW_REQUIRED`, `mergeable_state: blocked`.** As the 
author I cannot approve my own PR — this comment is informational. A maintainer 
with write access still needs to give the formal approval before merge.
   - **Upstream sync status, recorded for completeness (not a blocker to raise 
as a sync request):** this branch is currently `ahead_by=6`, `behind_by=28` 
against `dev` (compare status `diverged`, confirmed via `git rev-list` in a 
fresh worktree against latest `dev`). Since the current CI run is fully green 
on this head, there's no "CI failure that might be resolved by syncing" 
condition here — syncing is optional housekeeping before merge, not a 
troubleshooting step.
   
   **Overall assessment.** This is a correct, minimal, well-scoped bug fix. The 
mechanism is better than its own description suggests: because every 
`>19`-length formatter in `DateTimeUtils` embeds `ISO_LOCAL_TIME`, the 
per-field cache converges on a precision-agnostic formatter after a single 
refresh and never re-matches or fails again for that field — so this is not a 
per-record cost and not a swap to a different fixed precision that trades one 
failure for another. It changes no previously-produced value, only rescues rows 
that previously failed outright or were silently nulled, and the write path was 
never at risk. From a source-code perspective this is ready for maintainer 
review and merge; the remaining checklist is entirely process (undraft, get a 
maintainer approval) now that CI is green.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to