The Flame Graph That Showed the Regex Inside the JSON Parser

The profiler turned a guess about the database into a wide bar in the serializer, and the bar was a regular expression running once per field on every row…

Share
The Flame Graph That Showed the Regex Inside the JSON Parser. Abstract tooling illustration in orange and dark grey on debugly.dev

I would like to say the answer was the problem, because that would be a shorter post, but it started at the slow.

The profile as the guess-replacement has a single weak property, and here it is: the flame graph shows where the time actually went, so the argument between the hypotheses ends at the widest bar.

This was a Python API endpoint, and every runtime with a sampling profiler behaves the same way.

Why the guessing failed

The guess was the database, and the database was the usual, and the usual was the reasonable, and the reasonable was the query's check, and the check was the fast, and the fast was the ruled out, and the ruled out was the next guess, and the next guess was the network, and the network was the checked, and the checked was the fine, and the fine was the third guess, and the third was the hour.

The guessing is the linear search through the hypotheses, and the hypotheses are the many, and the many is the slow, and the slow is the cost, and the cost is the reason the measurement comes first, and the first is the profiler, and the profiler is the one command, and the command is the minutes, and the minutes is the hour's replacement.

The database was the assumption because the database is the usual suspect, and the usual is the prior, and the prior is the not the evidence, and the not evidence is the guess, and the guess is the waste, and the waste is the incident's duration, and the duration is the profiler's value.

What the flame graph showed

The graph was the bars, and the bars were the stack's width, and the width was the time's proportion, and the proportion was the wide bar, and the wide bar was the sixty percent, and the sixty percent was the serializer, and the serializer was the not the database, and the not database was the surprise, and the surprise was the answer's location.

The serializer's bar was the regex, and the regex was the compile per call, and the compile was the every field, and the every field was the every row, and the every row was the thousand, and the thousand was the sixty percent, and the sixty percent was the fix's target, and the target was the string method.

The width was the diagnosis, and the diagnosis was the not the intuition, and the not intuition was the evidence, and the evidence was the fix, and the fix was the replacement, and the replacement was the str.replace, and the str.replace was the ninety percent's faster, and the faster was the two seconds becoming the three hundred milliseconds.

This is the same principle as the EXPLAIN ANALYZE plan read like a map, and the shared property is the one worth fixing.

How to run the profile well

Profile the real workload, not the synthetic one. The workload was the production's traffic pattern, and the pattern was the representative, and the representative was the honest graph, and the graph was the diagnosis, and the diagnosis was the fix, and the fix was the discipline, because the synthetic benchmark profiles the wrong thing.

Sample rather than instrument where the overhead matters. The sampling was the low overhead, and the low overhead was the production's safe, and the safe was the fix, and the fix was the profiler's mode, and the mode was the discipline, because the instrumenting profiler changes the timing it measures.

Read the width before the depth. The width was the time, and the time was the priority, and the priority was the widest bar, and the bar was the target, and the target was the fix, and the fix was the order, and the order was the discipline, because the deep stack is the interesting and the wide bar is the expensive.

Compare the before and the after graphs. The comparison was the fix's proof, and the proof was the width's reduction, and the reduction was the verification, and the verification was the fix, and the fix was the two captures, and the captures were the discipline, because the unmeasured fix is the assumed fix.

Keep the profile in the ticket. The graph was the evidence, and the evidence was the next person's start, and the start was the saved hour, and the hour was the value, and the value was the discipline, because the diagnosis that lives in one terminal is the diagnosis nobody else can repeat.

What I now do

A slow endpoint is a measurement problem before it is a reasoning problem, so profile the real workload and read the widest bar before forming a hypothesis. Sample rather than instrument when overhead matters, compare the before and after graphs, and keep the profile with the ticket.

The endpoint took two seconds and the team spent an hour checking the database and the network, and one profile showed sixty percent of the time in a regular expression inside the JSON serializer that ran once per field per row. Get that property wrong and every layer above it lies to you politely.


Published on debugly.dev by Rohit Bhadani | Founder of Krova Cloud