The Great Dictionary Resize Mystery - A Historical Debugging Note
Documentation home · Current dictionary guide
Historical investigation. This narrative preserves an earlier debugging episode. Use the current dictionary guide and tests for present behavior.
Audience: contributors interested in debugging history and implementation lessons.
This note documents an old debugging session. It is not a description of the current TThreadSafeDictionary.CheckLoadFactor implementation.
Initial Problem#
The dictionary appeared to hang when trying to add items after reaching the load factor threshold (0.75). At the time, temporary logs showed:
...
Starting TestLargeDataSet
Running test #11
...
CheckLoadFactor: Current ratio: 0.81
CheckLoadFactor: Resizing needed
CheckLoadFactor: Resize complete
...The conclusion during that session was that the resize was not actually happening.
The Journey#
- Spent hours debugging the resize functionality
- Analyzed method linkage
- Added debug logging
- Removed and re-added
try..finallyblocks inprocedure TThreadSafeDictionary.CheckLoadFactor;
At the time, this temporary debug version was used:
procedure TThreadSafeDictionary.CheckLoadFactor;
begin
WriteLn(Format('CheckLoadFactor: Current ratio: %f', [FCount / Length(FBuckets)]));
if (FCount / Length(FBuckets)) > LOAD_FACTOR then
begin
WriteLn('CheckLoadFactor: Resizing needed');
try
Resize(Length(FBuckets) * 2);
finally
WriteLn('CheckLoadFactor: Resize complete');
end;
end;
end;Then it was rewritten as follows and appeared to work:
procedure TThreadSafeDictionary.CheckLoadFactor;
begin
WriteLn(Format('CheckLoadFactor: Current ratio: %f', [FCount / Length(FBuckets)]));
if (FCount / Length(FBuckets)) > LOAD_FACTOR then
begin
WriteLn('CheckLoadFactor: Resizing needed');
Resize(Length(FBuckets) * 2);
WriteLn('CheckLoadFactor: Resize complete');
end;
end;- Questioned our understanding of Pascal/Delphi
- Wrote detailed implementation fixes
The "Solution"#
- Go to top menu bar in Lazarus
- Click Run
- Click Clean and Build
What Actually Happened#
- Old compiled code was probably still in use
- Incremental builds weren't picking up all changes
- Full rebuild cleared out all old compiled units and started fresh
At the end, both versions worked.
The current dictionary source no longer contains this temporary WriteLn logging and does not define a DEBUG_LOGGING constant.
Key Learnings#
- Always try a clean build when behavior seems inexplicable
- Don't trust incremental builds when debugging weird issues
- Sometimes the simplest solution is the right one
- Hours of sophisticated debugging can be solved by basic build hygiene
The Irony#
The try..finally block was perfectly fine all along. We were debugging phantom issues from stale compiled code!
Best Practices Reminder#
- Clean build when:
- Behavior seems inconsistent
- Changes don't seem to take effect
- Different developers see different behavior
- Don't trust incremental builds during deep debugging sessions
- Add "Clean and Build" to your initial debugging checklist
---
Slow Performance Issue - Troubleshooting Log#
- Initially saw very slow performance
- Discovered excessive logging was the culprit
- Removed/reduced temporary logging for performance tests
- Performance for 100,000 items:
- Add: 31ms (0.31 microseconds per item)
- Find: 78ms (0.78 microseconds per item)
Machine: Processor 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz, 2304 Mhz, 8 Core(s), 16 Logical Processor(s)