1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.cache;
|
---|
3 |
|
---|
4 | import static org.junit.Assert.assertEquals;
|
---|
5 | import static org.junit.Assert.assertTrue;
|
---|
6 |
|
---|
7 | import java.io.IOException;
|
---|
8 | import java.net.URL;
|
---|
9 | import java.util.concurrent.ThreadPoolExecutor;
|
---|
10 | import java.util.concurrent.TimeUnit;
|
---|
11 | import java.util.concurrent.atomic.AtomicInteger;
|
---|
12 |
|
---|
13 | import org.apache.commons.jcs.access.behavior.ICacheAccess;
|
---|
14 | import org.junit.Rule;
|
---|
15 | import org.junit.Test;
|
---|
16 | import org.openstreetmap.josm.testutils.JOSMTestRules;
|
---|
17 | import org.openstreetmap.josm.tools.Utils;
|
---|
18 |
|
---|
19 | import 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 | */
|
---|
26 | public 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 | }
|
---|