It is a very helpful feature in our production. But I have one question: 
Volatile Read on the Hot Path — Could It Become a Bottleneck in 
Extreme-Throughput Scenarios?


Best
xingsuo-zbz

在 2026-04-09 18:51:12,"Jiangang Liu" <[email protected]> 写道:
>Thanks, Look_Y_Y. Excellent question — the unreliability of toString() is
>precisely what we focused our defensive design around. We employ *three
>layers of defense-in-depth*:
>
>   1.
>
>   *Smart detection + skip*: We use ClassValue<Boolean> for reflection
>   caching to *detect at class-load time* whether a class overrides
>   toString(). For classes that don't (which would output ClassName@hashCode),
>   we directly return a descriptive placeholder such as [MyEvent - no
>   toString() override], avoiding meaningless output. This detection is
>   O(1) and computed only once per class.
>   2.
>
>   *Time budget mechanism*: The tostring-budget-ms configuration (default
>   50ms/s) ensures that the *cumulative time* spent in toString() calls
>   does not exceed 50ms per second. Once the budget is exhausted, remaining
>   records within that second have their toString() skipped and are marked
>   as [toString() budget exceeded]. Even if one toString() call takes
>   200ms, it blocks the mailbox thread at most once — no further calls are
>   made for the rest of that second.
>   3.
>
>   *Complete exception isolation*: All toString() calls are wrapped in
>   try-catch. Exceptions are logged at DEBUG level, and the record content is
>   replaced with [toString() threw ExceptionType: message]. *Critically*,
>   super.collectAndCheckIfChained() — the normal record forwarding — *executes
>   before the sampling logic* (forward-first). So even if toString() throws
>   an exception or triggers an OOM, the data has already been forwarded
>   downstream. Business processing is never affected.
>   4.
>
>   *Regarding BinaryRowData and other binary formats*: This falls under the
>   dedicated Table/SQL optimization, which we explicitly list as the first
>   item in Future Work (Schema-Aware Formatting). In the initial version,
>   toString() will output a hex digest with a type hint — admittedly not
>   user-friendly for SQL scenarios, but it produces no errors or risks. This
>   also leaves room for follow-up community contributions.
>
>Best regards,
>Jiangang Liu
>
>Look_Y_Y <[email protected]> 于2026年4月9日周四 17:54写道:
>
>> I like the design. But one question: Relying on `toString()` in Production
>> — What About Exceptions, Deadlocks, and Meaningless Output?
>>
>> > Relying on `toString()` for data display in production jobs raises
>> several concerns: (1) Many user-defined POJOs either don't override
>> `toString()` (showing `com.foo.MyEvent@3a1b2c`) or have buggy
>> implementations that could throw exceptions or even deadlock (e.g.,
>> circular references with lazy-loading ORMs). (2) Flink's internal types
>> like `BinaryRowData` produce unreadable binary dumps. (3) Calling
>> `toString()` on user objects on the **mailbox thread** means a poorly
>> implemented `toString()` could block record processing. How do you ensure
>> this doesn't become a reliability risk in production?
>>
>> > 2026年4月9日 17:27,Jiangang Liu <[email protected]> 写道:
>> >
>> > A detail performance test is as follows:
>> >
>> https://docs.google.com/document/d/1NI5HCfnsWs9xyQ4MrdY6bY89phbegwS-JUPHA8kHKd4/edit?usp=sharing
>> >
>> > Jiangang Liu <[email protected]> 于2026年4月8日周三 17:36写道:
>> >
>> >> Hi, xiongraorao. Thanks for your question. We addressed this scalability
>> >> scenario systematically in the design:
>> >>
>> >>   1. *Multi-layer capacity limits ensure bounded memory*:
>> >>
>> >>
>> >>   -
>> >>
>> >>   At most *1,000 records* per subtask (BoundedSampleBuffer)
>> >>   -
>> >>
>> >>   At most *5,000 total records* per response (Coordinator applies
>> >>   proportional fair truncation)
>> >>   -
>> >>
>> >>   At most *10,000 characters* per record (max-record-length, truncated
>> >>   if exceeded)
>> >>   -
>> >>
>> >>   At most *5 concurrent sampling rounds* per JM (excess requests are
>> >>   rejected immediately with TOO_MANY_CONCURRENT_ROUNDS)
>> >>
>> >> Worst-case estimate: 5 concurrent rounds × 5,000 records × 10KB =
>> *250MB*.
>> >> In practice, toString() output averages far less than 10KB, and 5
>> >> concurrent rounds means only 5 vertices are being sampled at any given
>> >> moment. Given that JMs are typically configured with 4–8GB of heap
>> memory,
>> >> this upper bound is safe.
>> >>
>> >>   2.
>> >>
>> >>   *Guava Cache with expireAfterWrite*: The cache uses refresh-interval
>> >>   (default 60s) as TTL and entries expire automatically. There is no
>> >>   unbounded accumulation. Even if all 500 vertices have been sampled at
>> some
>> >>   point, caches with no new requests are GC'd after 60 seconds. In
>> practice,
>> >>   the number of vertices actively viewed in the WebUI at any given
>> moment is
>> >>   typically 3–5.
>> >>   3.
>> >>
>> >>   *Room to tighten further*: If the community feels it's necessary, we
>> >>   can add a rest.data-sampling.max-cached-vertices configuration (e.g.,
>> >>   default 20) using Guava's maximumSize to cap the number of cached
>> >>   vertices. This is trivial to add in the initial version.
>> >>   4.
>> >>
>> >>   *JM Failover*: Sampling results are ephemeral diagnostic data and do
>> >>   not require persistence. Upon JM failover, all in-flight rounds'
>> >>   CompletableFutures fail naturally (TM connections are severed), and
>> >>   the cache is destroyed along with the old JM instance. The new JM
>> starts
>> >>   with a clean state — users simply click again to trigger a fresh
>> sampling
>> >>   round. This is fully consistent with how FlameGraph behaves during JM
>> >>   failover, a pattern already validated in production.
>> >>
>> >>
>> >> 熊饶饶 <[email protected]> 于2026年4月8日周三 17:03写道:
>> >>
>> >>> Thanks for the flip. It is useful for users. I have only one question:
>> JM
>> >>> Memory Pressure Under High-Concurrency Sampling — Could It Cause OOM in
>> >>> Large-Scale Jobs?
>> >>>
>> >>>> 2026年3月24日 12:24,Jiangang Liu <[email protected]> 写道:
>> >>>>
>> >>>> Hi everyone,
>> >>>>
>> >>>> I would like to start a discussion on FLIP-570: Support Runtime Data
>> >>>> Sampling for Operators with WebUI Visualization [1].
>> >>>>
>> >>>> Inspecting intermediate data in a running Flink job is a common need
>> >>>> across development, data exploration, and troubleshooting. Today, the
>> >>>> only options are modifying the job (print() sink, log statements — all
>> >>>> require a restart) or deploying external infrastructure (extra Kafka
>> >>>> topics, debug sinks). Both are slow and disruptive for what is
>> >>>> essentially a "what does the data look like here?" question.
>> >>>>
>> >>>> FLIP-570 proposes native runtime data sampling, following the same
>> >>>> proven architecture pattern as FlameGraph (FLINK-13550). The key
>> ideas:
>> >>>>
>> >>>>  1. On-demand, round-scoped sampling at the output of any job vertex,
>> >>>>  triggered via REST API without job restart or topology modification.
>> >>>>  2. A new "Data Sample" tab in the WebUI with auto-polling, subtask
>> >>>>  selector, and status-driven display.
>> >>>>  3. Minimal overhead: zero when disabled; ~1.6% for the lightest ETL
>> >>>>  workloads when enabled-idle; <0.5% for typical production workloads.
>> >>>>  4. Safety by default: disabled by default, with rate limiting, time
>> >>>>  budget, buffer caps, and round-scoped auto-disable.
>> >>>>
>> >>>> For more details, please refer to the FLIP [1].
>> >>>>
>> >>>> Looking forward to your feedback and thoughts!
>> >>>>
>> >>>> [1]
>> >>>>
>> >>>
>> https://cwiki.apache.org/confluence/display/FLINK/FLIP-570%3A+Support+Runtime+Data+Sampling+for+Operators+with+WebUI+Visualization
>> >>>>
>> >>>> Best regards,
>> >>>> Jiangang Liu
>> >>>
>> >>>
>>
>>

Reply via email to