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 ProgressiveDownloader reporting wrong doubled values of downloa… #8579

Conversation

samoylenkodmitry
Copy link
Contributor

Fix ProgressiveDownloader reporting wrong doubled values of downloaded bytes and percent after 'No space left on device' IOException and retry.

This case added as a unit test in ProgressiveDownloaderTest download_afterSinkException_reportsValidProgress() method.

The main source of problem was that in CacheDataSink underlying file got removed after OutputStream.flush() failed in
closeCurrentOutputStream() method. This information doesn't propagate to CacheWriter logic. And wrong progress/bytesCached reported when user calls cache() method second time.

To fix this we need to propagate information about file getting removed and set initialized flag to false.

There was a corner case where OutputStream throws exception either in write() and in flush() methods. The second error was lost, but the second is an important one because in it a file was removed. To fix this case we need to replace the original exception with the second one instead of using Util.closeQuietly() method.

For context, the stacktrace when ENOSPC raised by device:

com.google.android.exoplayer2.upstream.cache.CacheDataSink$CacheDataSinkException: java.io.IOException: write failed: ENOSPC (No space left on device)
	at com.google.android.exoplayer2.upstream.cache.CacheDataSink.write(CacheDataSink.java:218)
	at com.google.android.exoplayer2.upstream.crypto.AesCipherDataSink.write(AesCipherDataSink.java:90)
	at com.google.android.exoplayer2.upstream.TeeDataSource.read(TeeDataSource.java:75)
	at com.google.android.exoplayer2.upstream.cache.CacheDataSource.read(CacheDataSource.java:600)
	at com.google.android.exoplayer2.upstream.cache.CacheWriter.readBlockToCache(CacheWriter.java:200)
	at com.google.android.exoplayer2.upstream.cache.CacheWriter.cache(CacheWriter.java:146)
	at com.google.android.exoplayer2.offline.ProgressiveDownloader$1.doWork(ProgressiveDownloader.java:125)
	at com.google.android.exoplayer2.offline.ProgressiveDownloader$1.doWork(ProgressiveDownloader.java:122)
	at com.google.android.exoplayer2.util.RunnableFutureTask.run(RunnableFutureTask.java:125)
	at com.google.android.exoplayer2.offline.-$$Lambda$_14QHG018Z6p13d3hzJuGTWnNeo.execute(lambda)
	at com.google.android.exoplayer2.offline.ProgressiveDownloader.download(ProgressiveDownloader.java:144)
	at ru.ivi.exodownloader.ExoDownloadTask$mRunnable$1.run(ExoDownloadTask.kt:65)
	at ru.ivi.utils.Assert.safelyRunTask(Assert.java:459)
	at ru.ivi.tools.NamedThreadFactory.lambda$newThread$0(NamedThreadFactory.java:46)
	at ru.ivi.tools.-$$Lambda$NamedThreadFactory$E7LhVUC0Lk5yoXiceXQQYfrrLWI.run(lambda)
	at java.lang.Thread.run(Thread.java:761)
Caused by: java.io.IOException: write failed: ENOSPC (No space left on device)
	at libcore.io.IoBridge.write(IoBridge.java:501)
	at java.io.FileOutputStream.write(FileOutputStream.java:316)
	at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
	at java.io.BufferedOutputStream.write(BufferedOutputStream.java:126)
	at com.google.android.exoplayer2.upstream.cache.CacheDataSink.write(CacheDataSink.java:212)
	... 15 more
Caused by: android.system.ErrnoException: write failed: ENOSPC (No space left on device)
	at libcore.io.Posix.writeBytes(Native Method)
	at libcore.io.Posix.write(Posix.java:273)
	at libcore.io.BlockGuardOs.write(BlockGuardOs.java:319)
	at libcore.io.IoBridge.write(IoBridge.java:496)
	... 19 more

But for better picture please see Unit Tests.
To reproduce the bag in this branch:

  1. In CacheWriter line 151 comment out initialized = false
  2. Run Unit Test ProgressiveDownloaderTest.download_afterSinkException_reportsValidProgress

…ded bytes and percent after 'No space left on device' `IOException` and retry.

This case added as unit test in `ProgressiveDownloaderTest` `download_afterSinkException_reportsValidProgress()` method.

The main source of problem was that in `CacheDataSink` underlying file got removed after `OutputStream.flush()` failed in
`closeCurrentOutputStream()` method. This information doesn't propagate to CacheWriter logic. And wrong progress/bytesCached reported when user calls `cache()` method second time.

To fix this we need to propagate information about file getting removed and set `initialized` flag to false.

There was a corner case where `OutputStream` throws exception either in `write()`` and in `flush()`` methods. The second error was lost, but second is a important one because in it a file was removed. To fix this case we need to replace original exception with second one instead of using `Util.closeQuietly()` method.
@ojw28
Copy link
Contributor

ojw28 commented Feb 23, 2021

Thanks for sending this! I agree that there's an issue here, although I concluded that the proposed solution was a little invasive. I have merged c067ee8 instead, which I think is sufficient to resolve the problem, so I will go ahead and close this. Please let us know if you still see the problem, either here or by filing a new issue!

As an aside, there are some other known issues affecting CacheWriter that we're looking at. These are summarised in #7326 (comment).

@ojw28 ojw28 closed this Feb 23, 2021
@google google locked and limited conversation to collaborators Apr 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants