Make insert size thresholds robust and thread safe - #1877
Merged
Merged
Conversation
Mirrors igv.js 96e3776. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Review of the insert size (TLEN) threshold estimate in
PEStats, prompted by igvteam/igv.js@96e3776, which fixed the equivalent code in igv.js. The load-order bug fixed there is not present here — desktop has always pooled samples across loads and recomputed — but the review turned up two other problems.Thread safety
track.load(frame)runs on a 5 thread pool, so in multi-locus view several tiles of the same track load at once, all writing the sharedpeStatsmap and the sharedPEStatsobjects with no synchronization. ConcurrentDoubleArrayList.addcould lose a sample or leave an uninitialized0.0in the pool (enough zeros drag the 0.5th percentile to 0 and silently disable small-insert coloring), andtoArray()'strimToSize()could swap the backing array under a concurrentadd, throwingArrayIndexOutOfBoundsExceptionorNumberIsTooLargeExceptionout ofloadTile— an error dialog, and a tile returned beforefinish().Each tile now samples into its own thread-confined
PEStats(PEStats.forLoad) and merges once when the load completes.merge,computeInsertSizeandcomputeExpectedOrientationare synchronized; the published thresholds arevolatileso the renderer reads them without a monitor. The shared map is aConcurrentHashMap.Robustness of the estimate
The thresholds are percentiles in the tails, so a region dense with proper-pair-flagged discordant reads could drag the maximum past every read worth flagging. Following igv.js:
Also
loadTilerecomputed the thresholds from the global preferences on every load, discarding percentiles set per-track through Set insert size options .... It now uses the track'sRenderOptions, which fall back to the same preferences when unset.OutOfRangeExceptionout of the load. It now means what it reads as: no read is colored as a small insert. The dialog accepts 0 accordingly.minTLENPercentile/maxTLENPercentilewere written to JSON sessions as numbers but read back withgetString, so a session carrying them failed to load. igv.js's legacyminFragmentLength/maxFragmentLengthare now accepted as aliases forminTLEN/maxTLEN.InsertSizeSettingsDialogrestored a Swing component dump instead of the previous value after a bad entry.Notes
Insert size coloring is not a useful metric for RNA data, so no accommodation is made for libraries whose large TLENs are genuine (intron-spanning pairs); desktop and igv.js behave identically here.
Three tests in
PEStatsTestcover the trimming, the per-load cap and the zero minimum; each was verified to fail without its fix. Full suite: 681 tests, 0 failures. User documentation for the estimator is drafted separately for igv.org.🤖 Generated with Claude Code