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