source: josm/trunk/test/unit/org/openstreetmap/josm/data/cache/HostLimitQueueTest.java@ 11444

Last change on this file since 11444 was 11444, checked in by wiktorn, 7 years ago

Fix disappearing download tasks.

When ThreadPoolExecutor is adapting it's size, it interrupts its threads from time to time. HostLimitQueue had a bug, that when interrupted when waiting to acquire lock, it didn't return the job back to the queue.

Added testcases that helped debug this and that verify, that we're actually doing host based limiting.

Sponsored by "Girl, interrupted" movie with Angelina Jolie.

See: #14166

File size: 6.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.cache;
3
4import static org.junit.Assert.assertEquals;
5import static org.junit.Assert.assertTrue;
6
7import java.io.IOException;
8import java.net.URL;
9import java.util.concurrent.ThreadPoolExecutor;
10import java.util.concurrent.TimeUnit;
11import java.util.concurrent.atomic.AtomicInteger;
12
13import org.apache.commons.jcs.access.behavior.ICacheAccess;
14import org.junit.Rule;
15import org.junit.Test;
16import org.openstreetmap.josm.testutils.JOSMTestRules;
17import org.openstreetmap.josm.tools.Utils;
18
19import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
20
21/**
22 * Simple tests for ThreadPoolExecutor / HostLimitQueue veryfing, that this pair works OK
23 * @author Wiktor Niesiobedzki
24 *
25 */
26public class HostLimitQueueTest {
27 /**
28 * Setup test.
29 */
30 @Rule
31 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
32 public JOSMTestRules test = new JOSMTestRules().preferences().timeout(20 * 1000);
33
34
35 private static ThreadPoolExecutor getNewThreadPoolExecutor(String nameFormat, int workers, int queueLimit) {
36 HostLimitQueue workQueue = new HostLimitQueue(queueLimit);
37 ThreadPoolExecutor executor = new ThreadPoolExecutor(
38 0, // 0 so for unused thread pools threads will eventually die, freeing also the threadpool
39 workers, // do not this number of threads
40 300, // keepalive for thread
41 TimeUnit.SECONDS,
42 workQueue,
43 Utils.newThreadFactory(nameFormat, Thread.NORM_PRIORITY)
44 );
45 workQueue.setExecutor(executor);
46 return executor;
47 }
48
49 /**
50 * Mock class for tests
51 */
52 static class Task extends JCSCachedTileLoaderJob<String, CacheEntry> {
53 private URL url;
54 private AtomicInteger counter;
55 private int id;
56
57 public Task(ICacheAccess<String, CacheEntry> cache, URL url, AtomicInteger counter, int id) {
58 super(cache, 1, 1, null);
59 this.url = url;
60 this.counter = counter;
61 this.id = id;
62 }
63
64 @Override
65 public void run() {
66 try {
67 Thread.sleep(1000);
68 } catch (InterruptedException e) {
69 } finally {
70 this.counter.incrementAndGet();
71 executionFinished();
72 }
73 }
74
75 @Override
76 public String getCacheKey() {
77 return "";
78 }
79
80 @Override
81 public URL getUrl() throws IOException {
82 return this.url;
83 }
84
85 @Override
86 protected CacheEntry createCacheEntry(byte[] content) {
87 return null;
88 }
89 }
90
91 /**
92 * Check if single threaded execution works properly
93 * @throws InterruptedException
94 * @throws IOException
95 */
96 @Test
97 public void test_single_thread_per_host() throws InterruptedException, IOException {
98 ThreadPoolExecutor tpe = getNewThreadPoolExecutor("test-%d", 3, 1);
99 ICacheAccess<String, CacheEntry> cache = JCSCacheManager.getCache("test", 3, 0, "");
100 AtomicInteger counter = new AtomicInteger(0);
101 long start = System.currentTimeMillis();
102 for (int i = 0; i < 10; i++) {
103 tpe.execute(new Task(cache, new URL("http://localhost/"+i), counter, i));
104 }
105 tpe.shutdown();
106 tpe.awaitTermination(15, TimeUnit.SECONDS); // at most it should take ~10 seconds, so after 15 it's already failed
107 long duration = System.currentTimeMillis() - start;
108 // check that all tasks were executed
109 assertEquals(10, counter.get());
110 // although there are 3 threads, we can make only 1 parallel call to localhost
111 // so it should take ~10 seconds to finish
112 // if it's shorter, it means that host limit does not work
113 assertTrue("Expected duration between 9 and 11 seconds not met. Actual duration: " + (duration /1000), duration < 11*1000 & duration > 9*1000 );
114 }
115
116 /**
117 * Check if two threaded execution work properly
118 * @throws InterruptedException
119 * @throws IOException
120 */
121 @Test
122 public void test_multiple_thread_per_host() throws InterruptedException, IOException {
123 ThreadPoolExecutor tpe = getNewThreadPoolExecutor("test-%d", 3, 2);
124 ICacheAccess<String, CacheEntry> cache = JCSCacheManager.getCache("test", 3, 0, "");
125 AtomicInteger counter = new AtomicInteger(0);
126 long start = System.currentTimeMillis();
127 for (int i = 0; i < 10; i++) {
128 tpe.execute(new Task(cache, new URL("http://hostlocal/"+i), counter, i));
129 }
130 tpe.shutdown();
131 tpe.awaitTermination(15, TimeUnit.SECONDS);
132 long duration = System.currentTimeMillis() - start;
133 // check that all tasks were executed
134 assertEquals(10, counter.get());
135 // although there are 3 threads, we can make only 2 parallel call to localhost
136 // so it should take ~5 seconds to finish
137 // if it's shorter, it means that host limit does not work
138 assertTrue("Expected duration between 4 and 6 seconds not met. Actual duration: " + (duration /1000), duration < 6*1000 & duration > 4*1000 );
139 }
140
141 /**
142 * Check two hosts
143 * @throws InterruptedException
144 * @throws IOException
145 */
146 @Test
147 public void test_two_hosts() throws InterruptedException, IOException {
148 ThreadPoolExecutor tpe = getNewThreadPoolExecutor("test-%d", 3, 1);
149 ICacheAccess<String, CacheEntry> cache = JCSCacheManager.getCache("test", 3, 0, "");
150 AtomicInteger counter = new AtomicInteger(0);
151 long start = System.currentTimeMillis();
152 for (int i = 0; i < 10; i++) {
153 String url = (i % 2 == 0) ? "http://localhost" : "http://hostlocal";
154 tpe.execute(new Task(cache, new URL(url+i), counter, i));
155 }
156 tpe.shutdown();
157 tpe.awaitTermination(15, TimeUnit.SECONDS);
158 long duration = System.currentTimeMillis() - start;
159 // check that all tasks were executed
160 assertEquals(10, counter.get());
161 // although there are 3 threads, we can make only 1 parallel per host, and we have 2 hosts
162 // so it should take ~5 seconds to finish
163 // if it's shorter, it means that host limit does not work
164 assertTrue("Expected duration between 4 and 6 seconds not met. Actual duration: " + (duration /1000), duration < 6*1000 & duration > 4*1000 );
165 }
166
167}
Note: See TracBrowser for help on using the repository browser.