1 package org.paneris.bibliomania.loadtest;
2
3 import java.io.BufferedReader;
4 import java.io.FileInputStream;
5 import java.io.FileReader;
6 import java.io.InputStream;
7 import java.io.ObjectInputStream;
8 import java.net.URL;
9 import java.util.Enumeration;
10 import java.util.Random;
11 import java.util.Vector;
12
13 import org.melati.util.UnexpectedExceptionException;
14
15 class HitStats {
16 public long totalToOpen, totalToFinish;
17 public int successes;
18 public int failures;
19
20
21 public void notifyTransaction(long started, long opened, long finished
22
23 ) {
24 totalToOpen += opened - started;
25 totalToFinish += finished - started;
26
27 ++successes;
28 }
29
30 public void notifyFailure() {
31 ++failures;
32 }
33
34 public long meanToOpen() {
35 return successes == 0 ? 0 : totalToOpen / successes;
36 }
37
38 public long meanToFinish() {
39 return successes == 0 ? 0 : totalToFinish / successes;
40 }
41 }
42
43 class ExponentialDistribution {
44 private double minusMean;
45
46 public ExponentialDistribution(double mean) {
47 minusMean = -mean;
48 }
49
50 public double sample() {
51 for (;;) {
52 float x = LoadTest.random.nextFloat();
53 if (x != 0.)
54 return minusMean * Math.log(x);
55 }
56 }
57 }
58
59 class BitBucket extends Thread {
60 private String url;
61 private HitStats stats;
62
63 public BitBucket(String url, HitStats stats) {
64 this.url = url;
65 this.stats = stats;
66 }
67
68 private static final byte[] b = new byte[4096];
69
70 public void run() {
71 System.err.println(url);
72 try {
73 long started = System.currentTimeMillis();
74 InputStream i = new URL(url).openStream();
75 long opened = System.currentTimeMillis();
76 while (i.read(b) != -1);
77 long finished = System.currentTimeMillis();
78
79 stats.notifyTransaction(started, opened, finished);
80 }
81 catch (Exception e) {
82
83 stats.notifyFailure();
84 }
85 }
86 }
87
88 abstract class Level {
89 abstract String urlFromSection(BookDesc book);
90 String url(BookDesc book) {
91 return book.sectiongroup + "/" + urlFromSection(book);
92 }
93 }
94
95 class SectionLevel extends Level {
96 String urlFromSection(BookDesc book) {
97 return "" + book.section;
98 }
99
100 public String toString() {
101 return "sec";
102 }
103 }
104
105 class AuthorLevel extends Level {
106 String urlFromSection(BookDesc book) {
107 return book.section + "/" + book.author;
108 }
109
110 public String toString() {
111 return "aut";
112 }
113 }
114
115 class BookLevel extends Level {
116 String urlFromSection(BookDesc book) {
117 return book.section + "/" + book.author + "/" + book.book;
118 }
119
120 public String toString() {
121 return "bok";
122 }
123 }
124
125 class ChapterLevel extends Level {
126 String urlFromSection(BookDesc book) {
127 ChapterDesc c =
128 book.chapters[LoadTest.random.nextInt(book.chapters.length)];
129 return book.section + "/" + book.author + "/" + book.book + "/" +
130 c.chapter + "/" + (LoadTest.random.nextInt(c.pages) + 1) + ".html";
131 }
132
133 public String toString() {
134 return "cha";
135 }
136 }
137
138 abstract class Urler {
139 abstract String urlPath(Level level, BookDesc book);
140 }
141
142 class ReadUrler extends Urler {
143 String urlPath(Level level, BookDesc book) {
144 return level.url(book);
145 }
146
147 public String toString() {
148 return "read";
149 }
150 }
151
152 class SearchUrler extends Urler {
153
154 static String[] words;
155
156 static {
157 try {
158 Vector w = new Vector();
159 BufferedReader r = new BufferedReader(new FileReader("/usr/share/dict/words"));
160 for (;;) {
161 String s = r.readLine();
162 if (s == null)
163 break;
164 w.addElement(s);
165 }
166 words = new String[w.size()];
167 w.copyInto(words);
168 }
169 catch (Exception e) {
170 e.printStackTrace();
171 System.exit(1);
172 }
173 }
174
175 static String randomWord() {
176 return words[LoadTest.random.nextInt(words.length)];
177 }
178
179 int searchgroup;
180 int numwords;
181
182 SearchUrler(int searchgroup, int numwords) {
183 this.searchgroup = searchgroup;
184 this.numwords = numwords;
185 }
186
187 String urlPath(Level level, BookDesc book) {
188 StringBuffer qt = new StringBuffer();
189
190 qt.append(randomWord());
191
192 for (int i = 1; i < numwords; ++i) {
193 qt.append("+");
194 qt.append(randomWord());
195 }
196
197 return
198 "b/org.paneris.bibliomania.Search/" + searchgroup + "/" +
199 level.urlFromSection(book) + "?" + "queryText=" + qt;
200 }
201
202 public String toString() {
203 return "search(" + numwords + ")";
204 }
205 }
206
207 class ReqType {
208 HitStats stats = new HitStats();
209 Urler urler;
210 Level level;
211
212 ReqType(Urler urler, Level level) {
213 this.urler = urler;
214 this.level = level;
215 }
216
217 void go(BookDesc book) {
218 new BitBucket(LoadTest.urlPrefix + urler.urlPath(level, book), stats).
219 start();
220 }
221
222 public String toString() {
223 return urler + "/" + level;
224 }
225 }
226
227 public class LoadTest {
228
229 static Random random = new Random();
230 static String urlPrefix;
231
232 private long runtimeMillis;
233 private boolean keepGoing;
234 private ExponentialDistribution wait;
235 private DiscreteDistribution types = new DiscreteDistribution();
236 private int searchsectiongroup;
237
238 public LoadTest(long runtimeMillis, int hitsPerSec, int kSearch) {
239 this.runtimeMillis = runtimeMillis;
240 wait = new ExponentialDistribution(1000 / hitsPerSec);
241
242 types = new DiscreteDistribution();
243
244 Urler
245 read = new ReadUrler(),
246 search1 = new SearchUrler(searchsectiongroup, 1),
247 search2 = new SearchUrler(searchsectiongroup, 2);
248
249
250 Level
251 sec = new SectionLevel(),
252 aut = new AuthorLevel(),
253 bok = new BookLevel(),
254 cha = new ChapterLevel();
255
256 types.add(new ReqType(read, sec), 6);
257 types.add(new ReqType(read, aut), 12);
258 types.add(new ReqType(read, bok), 18);
259 types.add(new ReqType(read, cha), 37);
260
261
262
263
264
265
266
267
268 }
269
270 private class Stopper extends Thread {
271 public void run() {
272 try {
273 Thread.sleep(runtimeMillis);
274 }
275 catch (Exception e) {
276 throw new UnexpectedExceptionException(e);
277 }
278
279 keepGoing = false;
280 }
281 }
282
283 public static String pad(String it, int w) {
284 StringBuffer s = new StringBuffer(w);
285 while (s.length() + it.length() < w)
286 s.append(' ');
287 s.append(it);
288 return s.toString();
289 }
290
291 public static String pad(long l, int w) {
292 return pad("" + l, w);
293 }
294
295 public void run() throws Exception {
296 keepGoing = true;
297 new Stopper().start();
298
299 while (keepGoing) {
300 Thread.sleep((long)wait.sample());
301 ((ReqType)types.sample()).go((BookDesc)extractTree.books.sample());
302 }
303
304 System.err.println("stopping");
305 Thread.sleep(5);
306
307 System.out.println();
308 System.out.println(" win open end fail");
309 for (Enumeration h = types.elements(); h.hasMoreElements();) {
310 ReqType hit = (ReqType)h.nextElement();
311 System.out.print(hit.toString());
312 System.out.println(
313 pad(hit.stats.successes, 9) + " " +
314 pad(hit.stats.meanToOpen(), 5) + " " +
315 pad(hit.stats.meanToFinish(), 5) + " " +
316 pad(hit.stats.failures, 5) + " ");
317 }
318 }
319
320 static ExtractTree extractTree;
321
322 public static void main(String[] args) throws Exception {
323 ObjectInputStream i =
324 new ObjectInputStream(new FileInputStream("books.ser"));
325
326 extractTree = (ExtractTree)i.readObject();
327
328 i.close();
329
330
331
332
333
334
335
336 urlPrefix = "http://bibliomania.com/";
337 new LoadTest(new Integer(40) * 1000L,
338 new Integer(200),
339 new Integer(32)).run();
340 }
341 }