-
Notifications
You must be signed in to change notification settings - Fork 587
HDDS-4429. Create unit test for SimpleContainerDownloader. #1551
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
Changes from all commits
9e15cf3
1e96edd
17e50ae
c149ce4
125f8ee
9adbce2
9eca410
a7fc88b
e8d07cc
62c34d7
c54424b
38eee8a
e5ee7b6
2b66437
a280682
2cc31cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,28 +21,88 @@ | |||||||||||||
| import java.nio.file.Path; | ||||||||||||||
| import java.nio.file.Paths; | ||||||||||||||
| import java.util.ArrayList; | ||||||||||||||
| import java.util.Arrays; | ||||||||||||||
| import java.util.List; | ||||||||||||||
| import java.util.concurrent.CompletableFuture; | ||||||||||||||
| import java.util.concurrent.ExecutionException; | ||||||||||||||
| import java.util.concurrent.TimeUnit; | ||||||||||||||
|
|
||||||||||||||
| import org.apache.hadoop.hdds.conf.ConfigurationSource; | ||||||||||||||
| import org.apache.hadoop.hdds.conf.OzoneConfiguration; | ||||||||||||||
| import org.apache.hadoop.hdds.protocol.DatanodeDetails; | ||||||||||||||
| import org.apache.hadoop.hdds.protocol.MockDatanodeDetails; | ||||||||||||||
|
|
||||||||||||||
| import org.junit.Assert; | ||||||||||||||
| import org.junit.Test; | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Test container downloader. | ||||||||||||||
| /* | ||||||||||||||
| * Test SimpleContainerDownloader. | ||||||||||||||
| */ | ||||||||||||||
| public class TestSimpleContainerDownloader { | ||||||||||||||
|
|
||||||||||||||
| private static final String SUCCESS_PATH = "downloaded"; | ||||||||||||||
|
|
||||||||||||||
| @Test | ||||||||||||||
| public void testGetContainerDataFromReplicasHappyPath() throws Exception { | ||||||||||||||
|
|
||||||||||||||
| //GIVEN | ||||||||||||||
| List<DatanodeDetails> datanodes = createDatanodes(); | ||||||||||||||
|
|
||||||||||||||
| SimpleContainerDownloader downloader = | ||||||||||||||
| createDownloaderWithPredefinedFailures(true); | ||||||||||||||
|
|
||||||||||||||
| //WHEN | ||||||||||||||
| final Path result = | ||||||||||||||
| downloader.getContainerDataFromReplicas(1L, datanodes) | ||||||||||||||
| .get(1L, TimeUnit.SECONDS); | ||||||||||||||
|
|
||||||||||||||
| //THEN | ||||||||||||||
| Assert.assertEquals(datanodes.get(0).getUuidString(), result.toString()); | ||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, these checks are no longer valid after 5e8aaee since Lines 79 to 84 in 0aca5c7
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking into it. Seems I stepped in my own trap ;-) |
||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| @Test | ||||||||||||||
| public void testGetContainerDataFromReplicasDirectFailure() | ||||||||||||||
| throws Exception { | ||||||||||||||
|
|
||||||||||||||
| //GIVEN | ||||||||||||||
| List<DatanodeDetails> datanodes = createDatanodes(); | ||||||||||||||
|
|
||||||||||||||
| SimpleContainerDownloader downloader = | ||||||||||||||
| createDownloaderWithPredefinedFailures(true, datanodes.get(0)); | ||||||||||||||
|
|
||||||||||||||
| //WHEN | ||||||||||||||
| final Path result = | ||||||||||||||
| downloader.getContainerDataFromReplicas(1L, datanodes) | ||||||||||||||
| .get(1L, TimeUnit.SECONDS); | ||||||||||||||
|
|
||||||||||||||
| //THEN | ||||||||||||||
| //first datanode is failed, second worked | ||||||||||||||
| Assert.assertEquals(datanodes.get(1).getUuidString(), result.toString()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| @Test | ||||||||||||||
| public void testGetContainerDataFromReplicasAsyncFailure() throws Exception { | ||||||||||||||
|
|
||||||||||||||
| //GIVEN | ||||||||||||||
| List<DatanodeDetails> datanodes = createDatanodes(); | ||||||||||||||
|
|
||||||||||||||
| SimpleContainerDownloader downloader = | ||||||||||||||
| createDownloaderWithPredefinedFailures(false, datanodes.get(0)); | ||||||||||||||
|
|
||||||||||||||
| //WHEN | ||||||||||||||
| final Path result = | ||||||||||||||
| downloader.getContainerDataFromReplicas(1L, datanodes) | ||||||||||||||
| .get(1L, TimeUnit.SECONDS); | ||||||||||||||
|
|
||||||||||||||
| //THEN | ||||||||||||||
| //first datanode is failed, second worked | ||||||||||||||
| Assert.assertEquals(datanodes.get(1).getUuidString(), result.toString()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Test if different datanode is used for each download attempt. | ||||||||||||||
| */ | ||||||||||||||
| @Test(timeout = 1000L) | ||||||||||||||
| @Test(timeout = 10_000L) | ||||||||||||||
| public void testRandomSelection() | ||||||||||||||
| throws ExecutionException, InterruptedException { | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -62,24 +122,79 @@ protected CompletableFuture<Path> downloadContainer( | |||||||||||||
| } | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| //WHEN executed, THEN at least once the second datanode should be returned. | ||||||||||||||
| //WHEN executed, THEN at least once the second datanode should be | ||||||||||||||
| //returned. | ||||||||||||||
| for (int i = 0; i < 10000; i++) { | ||||||||||||||
| Path path = downloader.getContainerDataFromReplicas(1L, datanodes).get(); | ||||||||||||||
| Path path = | ||||||||||||||
| downloader.getContainerDataFromReplicas(1L, datanodes).get(); | ||||||||||||||
| if (path.toString().equals(datanodes.get(1).getUuidString())) { | ||||||||||||||
| return; | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| //there is 1/2^10_000 chance for false positive, which is practically 0. | ||||||||||||||
| //there is 1/3^10_000 chance for false positive, which is practically 0. | ||||||||||||||
| Assert.fail( | ||||||||||||||
| "Datanodes are selected 10000 times but second datanode was never " | ||||||||||||||
| + "used."); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| /** | ||||||||||||||
| * Creates downloader which fails with datanodes in the arguments. | ||||||||||||||
| * | ||||||||||||||
| * @param directException if false the exception will be wrapped in the | ||||||||||||||
| * returning future. | ||||||||||||||
| */ | ||||||||||||||
| private SimpleContainerDownloader createDownloaderWithPredefinedFailures( | ||||||||||||||
| boolean directException, | ||||||||||||||
| DatanodeDetails... failedDatanodes | ||||||||||||||
| ) { | ||||||||||||||
|
|
||||||||||||||
| ConfigurationSource conf = new OzoneConfiguration(); | ||||||||||||||
|
|
||||||||||||||
| final List<DatanodeDetails> datanodes = | ||||||||||||||
| Arrays.asList(failedDatanodes); | ||||||||||||||
|
|
||||||||||||||
| return new SimpleContainerDownloader(conf, null) { | ||||||||||||||
|
|
||||||||||||||
| //for retry testing we use predictable list of datanodes. | ||||||||||||||
| @Override | ||||||||||||||
| protected List<DatanodeDetails> shuffleDatanodes( | ||||||||||||||
| List<DatanodeDetails> sourceDatanodes | ||||||||||||||
| ) { | ||||||||||||||
| //turn off randomization | ||||||||||||||
| return sourceDatanodes; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| @Override | ||||||||||||||
| protected CompletableFuture<Path> downloadContainer( | ||||||||||||||
| long containerId, | ||||||||||||||
| DatanodeDetails datanode | ||||||||||||||
| ) throws Exception { | ||||||||||||||
|
|
||||||||||||||
| if (datanodes.contains(datanode)) { | ||||||||||||||
| if (directException) { | ||||||||||||||
| throw new RuntimeException("Unavailable datanode"); | ||||||||||||||
| } else { | ||||||||||||||
| return CompletableFuture.supplyAsync(() -> { | ||||||||||||||
| throw new RuntimeException("Unavailable datanode"); | ||||||||||||||
| }); | ||||||||||||||
| } | ||||||||||||||
| } else { | ||||||||||||||
|
|
||||||||||||||
| //path includes the dn id to make it possible to assert. | ||||||||||||||
| return CompletableFuture.completedFuture( | ||||||||||||||
| Paths.get(datanode.getUuidString())); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| } | ||||||||||||||
| }; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private List<DatanodeDetails> createDatanodes() { | ||||||||||||||
| List<DatanodeDetails> datanodes = new ArrayList<>(); | ||||||||||||||
| datanodes.add(MockDatanodeDetails.randomDatanodeDetails()); | ||||||||||||||
| datanodes.add(MockDatanodeDetails.randomDatanodeDetails()); | ||||||||||||||
| datanodes.add(MockDatanodeDetails.randomDatanodeDetails()); | ||||||||||||||
| return datanodes; | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if replication was slow, the previous version may have closed the client prematurely?