Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue with resource management in Jpeg compressed GeoTiffs #3249

Merged
merged 3 commits into from
Jun 4, 2020

Conversation

lossyrob
Copy link
Member

@lossyrob lossyrob commented Jun 4, 2020

Overview

This PR fixes an issue with resource handling in JPEG compressed GeoTiffs.

Reading JPEGs with ImageIO is known to cause issues. The way the JPEG decompression works does not close down resources sufficiently and can cause an error like this

Cause: java.nio.file.FileSystemException: /var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/imageio8791917777763522780.tmp: Too many open files in system

The first commit in this PR adds a unit test downloads a JPEG-compressed GeoTiff from OpenAerialMap. It then performs many parallel reads in a loop, causing a "too many files opened" exception with the current code.

The second commit modifies the JpegDecompression to always create and close the ImageReader resources as needed. This may cause the code to run slower, though I will note that the unit test running against a Deflate version of the GeoTiff ran successfully in 50 seconds, and it running against a (pre-downloaded) JPEG compressed version with the fix ran in 55 seconds.

Checklist

  • docs/CHANGELOG.rst updated, if necessary
  • Module Hierarcy updated, if necessary
  • docs guides update, if necessary
  • New user API has useful Scaladoc strings
  • Unit tests added for bug-fix or new feature

Related to raster-foundry/raster-foundry#4658

@lossyrob lossyrob force-pushed the fix/rde/jpeg-close-resource branch from 42876ec to 36f13c6 Compare June 4, 2020 01:53

val parList = (1 to 10000).toList.par
// TODO: Replace with java.util.concurrent.ForkJoinPool once we drop 2.11 support.
val forkJoinPool = new scala.concurrent.forkjoin.ForkJoinPool(50)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that using java.util.concurrent.ForkJoinPool causes Scala 2.11 to throw a compiler error. SemVer!

@lossyrob
Copy link
Member Author

lossyrob commented Jun 4, 2020

The unit test does not seem to be failing on Circle CI. I'm think this may be due to the relative lack of CPU power on the CI machine to cause enough of the reads to happen in parallel so that it triggers the 'too many files open' failure.

Looking for someone to verify that the test in 36f13c6 fails on their machine.

@lossyrob
Copy link
Member Author

lossyrob commented Jun 4, 2020

The unit test downloads a larger file (44MB) and takes about 50 second to run on my machine. Do we think that it should be marked as ignore in an additional commit, so that the test remains there to check but does not run by default? Or should we just keep the test in the suite as is?

I tried with a smaller GeoTiff that was already contained in the repository, but was unable to trigger the failure with that GeoTiff.

@lossyrob lossyrob changed the title [WIP] Add unit test to prove issue with Jpeg compressed GeoTiffs. Fix issue with resource management in Jpeg compressed GeoTiffs Jun 4, 2020
@pomadchin pomadchin self-requested a review June 4, 2020 03:07
@pomadchin pomadchin self-assigned this Jun 4, 2020
@pomadchin
Copy link
Member

36f13c6 fails on my machine 👍

@pomadchin
Copy link
Member

Another reason why CircleCI is happy is that it has more file descriptors available, and the test should be more agressive to make it fail.

Copy link
Member

@pomadchin pomadchin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can confirm that this PR fixes a bug and after applying it on my machine the test passes.
It looks like CircleCI has more file descriptors opened so to catch it.

I think we can merge it as is (after fixing package name and a filename). It looks like it didn't drammatically increase the CI runtime.

Thanks Rob 🎉

@@ -0,0 +1,74 @@
import geotrellis.raster._
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a package name for the test?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦 I guess it's been that long since I've done Scala development....

import sys.process._


class JPEGLoadGeoTiffReaderSpec extends FunSpec
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be aligned with the file name in this case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


override def afterAll = purge

describe("Reading GeoTiffs with JPEG compression") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you feel about adding a comment somewhere with a link to this issue, so we'll know the context about it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call!

lossyrob added 3 commits June 4, 2020 13:00
This unit test downloads a JPEG-compressed GeoTiff from OpenAerialMap. It then performs many parallel reads in a loop, causing a "too many files opened" exception.

Signed-off-by: Rob Emanuele <[email protected]>
Before this change, JpegDecompressor held onto an instance of a ImageReader, and used setInput
to set the segment bytes for individual segment read. This method was not cleaning up any of the resources
used, which caused issues where too many files were being opened up during parallelized reads.
This change creates a new ImageReader per segment read, and cleans up any InputStream and ImageReader resources
after each read.

Signed-off-by: Rob Emanuele <[email protected]>
Signed-off-by: Rob Emanuele <[email protected]>
@lossyrob lossyrob force-pushed the fix/rde/jpeg-close-resource branch from bff27a9 to c5bca8a Compare June 4, 2020 17:00
@lossyrob lossyrob requested a review from pomadchin June 4, 2020 17:02
@lossyrob
Copy link
Member Author

lossyrob commented Jun 4, 2020

@pomadchin pushed rebased changes to the unit test commit to address your comments. Thank you!

Copy link
Member

@pomadchin pomadchin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, thanks! 🚀 Merging once CircleCI is completely green.

@pomadchin pomadchin merged commit dafceaf into locationtech:master Jun 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants