001: /*
002: * Copyright (c) 2001 by Matt Welsh and The Regents of the University of
003: * California. All rights reserved.
004: *
005: * Permission to use, copy, modify, and distribute this software and its
006: * documentation for any purpose, without fee, and without written agreement is
007: * hereby granted, provided that the above copyright notice and the following
008: * two paragraphs appear in all copies of this software.
009: *
010: * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
011: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
012: * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
013: * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
014: *
015: * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
016: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
017: * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
018: * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
019: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
020: *
021: * Author: Matt Welsh <mdw@cs.berkeley.edu>
022: *
023: */
024:
025: package seda.apps.Haboob.bottleneck;
026:
027: import seda.apps.Haboob.*;
028: import seda.apps.Haboob.http.*;
029: import seda.sandStorm.api.*;
030: import seda.sandStorm.api.internal.*;
031: import seda.sandStorm.core.*;
032: import seda.sandStorm.lib.http.*;
033: import seda.sandStorm.lib.aSocket.*;
034: import seda.sandStorm.lib.aDisk.*;
035: import seda.util.*;
036: import java.io.*;
037: import java.util.*;
038:
039: /**
040: * An intentional bottleneck stage, used for demonstrating load conditioning.
041: * Does some I/O and CPU crunching to generate a dynamic web page; also
042: * provides an adaptive load shedding controller that adjusts the stage's
043: * queue threshold to meet a response time target. All of this is described
044: * in the SOSP'01 paper on SEDA, found at
045: * http://www.cs.berkeley.edu/~mdw/proj/seda/
046: *
047: * This version relies upon the system-supplied response time controller.
048: *
049: */
050: public class Bottleneck implements EventHandlerIF, HaboobConst {
051:
052: private static final boolean DEBUG = false;
053: private static final boolean VERBOSE = false;
054:
055: private static final int OUTPUT_STATIC_PAGE_SIZE = 8192;
056:
057: private SinkIF sendSink;
058: private Hashtable ht;
059: private Random rand;
060: private httpOKResponse static_page_response;
061:
062: // If true, allocate a large byte array and insert into a hashtable
063: private static final boolean BOTTLENECK_ALLOC = false;
064: private static final int MAX_ALLOC_SIZE = 81920;
065:
066: // If true, sleep
067: private static final boolean BOTTLENECK_SLEEP = false;
068: private static final long SLEEP_TIME = 100;
069:
070: // If true, read data from file and process sums
071: private static final boolean BOTTLENECK_PROCESSFILE = true;
072: // If true, generate random data and process sums.
073: private static final boolean BOTTLENECK_PROCESSRANDOM = false;
074: private static final String RANDOM_FILE = "/scratch/mdw/specweb99-runs/cgi-bin/random.data";
075: private static final int NUM_RUNS = 50;
076: private static final int NUM_SUMS = 50;
077: private static final int NUM_BYTES_TO_READ = 100;
078: private volatile static int sum = 0;
079: private static byte data[] = new byte[NUM_BYTES_TO_READ];
080:
081: // If true, degrade quality of responses to meet RT target
082: private static final boolean BOTTLENECK_DEGRADE = false;
083: private static final boolean BOTTLENECK_DEGRADE_RTCON = false;
084: private static final int BOTTLENECK_DEGRADE_RTCON_THRESH = 10;
085: private static double MIN_QUALITY = 0.01;
086: private static double MAX_QUALITY = 1.0;
087: private static double RTCON_DISABLE_QUALITY = 0.2;
088: private static double quality = MAX_QUALITY;
089: private static double VERY_HIGH_WATER = 1.5;
090: private static double HIGH_WATER = 1.1;
091: private static double LOW_WATER = 0.8;
092: private static double MEDIUM_WATER = 0.5;
093: private static double VERY_LOW_WATER = 0.0001;
094: private static double BOTTLENECK_DEGRADE_ADDITIVE_INCREASE = 0.01;
095: private static double BOTTLENECK_DEGRADE_MULTIPLICATIVE_DECREASE = 2.0;
096: private static double BOTTLENECK_DEGRADE_COUNT = 100;
097:
098: private StageStatsIF stats;
099: private ResponseTimeControllerIF rtcon;
100: private double targetRT;
101: private int degrade_count = 0;
102: private int degrade_min_count = 0;
103:
104: public void init(ConfigDataIF config) throws Exception {
105: SinkIF mysink = config.getStage().getSink();
106:
107: stats = config.getStage().getWrapper().getStats();
108: rtcon = config.getStage().getWrapper()
109: .getResponseTimeController();
110: if (rtcon != null) {
111: System.err.println("** BOTTLENECK: SETTING TARGET");
112: targetRT = rtcon.getTarget();
113: rtcon.disable();
114: }
115:
116: sendSink = config.getManager().getStage(HTTP_SEND_STAGE)
117: .getSink();
118: ht = new Hashtable();
119: rand = new Random();
120: System.err
121: .println("Bottleneck stage initialized, BOTTLENECK_DEGRADE="
122: + BOTTLENECK_DEGRADE
123: + ", BOTTLENECK_DEGRADE_RTCON="
124: + BOTTLENECK_DEGRADE_RTCON);
125:
126: }
127:
128: public void destroy() {
129: }
130:
131: public void handleEvent(QueueElementIF item) {
132: if (DEBUG)
133: System.err.println("Bottleneck: GOT QEL: " + item);
134:
135: if (item instanceof httpRequest) {
136: HaboobStats.numRequests++;
137:
138: httpRequest req = (httpRequest) item;
139: if (req.getRequest() != httpRequest.REQUEST_GET) {
140: HaboobStats.numErrors++;
141: sendSink
142: .enqueue_lossy(new httpResponder(
143: new httpBadRequestResponse(req,
144: "Only GET requests supported at this time"),
145: req, true));
146: return;
147: }
148:
149: // Do bottleneck work
150: long t1, t2;
151: if (VERBOSE)
152: t1 = System.currentTimeMillis();
153: doBottleneck();
154: if (VERBOSE) {
155: t2 = System.currentTimeMillis();
156: System.err.println("Bottleneck: " + (t2 - t1) + " ms");
157: }
158:
159: double ninetiethRT = stats.get90thRT();
160: if (BOTTLENECK_DEGRADE
161: && (++degrade_count >= BOTTLENECK_DEGRADE_COUNT)) {
162: degrade_count = 0;
163:
164: if (ninetiethRT < (VERY_LOW_WATER * targetRT)) {
165:
166: degrade_min_count = 0;
167: quality *= BOTTLENECK_DEGRADE_MULTIPLICATIVE_DECREASE;
168: if (quality > MAX_QUALITY)
169: quality = MAX_QUALITY;
170: if (BOTTLENECK_DEGRADE_RTCON) {
171: rtcon.disable();
172: }
173:
174: } else if (ninetiethRT < (LOW_WATER * targetRT)) {
175:
176: degrade_min_count = 0;
177: quality += BOTTLENECK_DEGRADE_ADDITIVE_INCREASE;
178: if (quality > MAX_QUALITY)
179: quality = MAX_QUALITY;
180: if (BOTTLENECK_DEGRADE_RTCON
181: && quality >= RTCON_DISABLE_QUALITY) {
182: rtcon.disable();
183: }
184: //if (BOTTLENECK_DEGRADE_RTCON) {
185: // rtcon.disable();
186: //}
187:
188: } else if (ninetiethRT < (MEDIUM_WATER * targetRT)) {
189:
190: degrade_min_count = 0;
191: if (BOTTLENECK_DEGRADE_RTCON) {
192: rtcon.disable();
193: }
194:
195: } else if (ninetiethRT > (VERY_HIGH_WATER * targetRT)) {
196:
197: if (BOTTLENECK_DEGRADE_RTCON) {
198: rtcon.enable();
199: }
200:
201: quality /= BOTTLENECK_DEGRADE_MULTIPLICATIVE_DECREASE;
202: if (quality <= MIN_QUALITY) {
203: quality = MIN_QUALITY;
204: }
205:
206: } else if (ninetiethRT > (HIGH_WATER * targetRT)) {
207:
208: quality /= BOTTLENECK_DEGRADE_MULTIPLICATIVE_DECREASE;
209: if (quality <= MIN_QUALITY) {
210: quality = MIN_QUALITY;
211:
212: if (BOTTLENECK_DEGRADE_RTCON) {
213: degrade_min_count++;
214: if (degrade_min_count >= BOTTLENECK_DEGRADE_RTCON_THRESH) {
215: rtcon.enable();
216: }
217: }
218: }
219: }
220: System.err.println("Bottleneck: 90th RT " + ninetiethRT
221: + ", target " + targetRT + ", quality now "
222: + quality);
223: }
224:
225: // Send response
226: String respstr = "BOTTLENECK: 90thRT " + ninetiethRT
227: + " targetRT " + targetRT + " quality " + quality
228: + "\n\n";
229: byte respstrbytes[] = respstr.getBytes();
230: httpOKResponse resp = new httpOKResponse("text/plain",
231: OUTPUT_STATIC_PAGE_SIZE);
232: BufferElement payload = resp.getPayload();
233: byte paydata[] = payload.data;
234: System.arraycopy(respstrbytes, 0, paydata, payload.offset,
235: respstrbytes.length);
236: httpResponder respd = new httpResponder(resp, req, false);
237: HttpSend.sendResponse(respd);
238: return;
239:
240: } else if (item instanceof SinkClosedEvent) {
241: // Ignore
242:
243: } else {
244: System.err.println("StaticPage: Got unknown event type: "
245: + item);
246: }
247:
248: }
249:
250: public void handleEvents(QueueElementIF items[]) {
251: if (DEBUG)
252: System.err.println("Bottleneck: " + Thread.currentThread()
253: + " got " + items.length + " events");
254:
255: for (int i = 0; i < items.length; i++) {
256: handleEvent(items[i]);
257: }
258: }
259:
260: private void doBottleneck() {
261:
262: if (BOTTLENECK_ALLOC) {
263: // Allocate big chunk of memory and stash it away
264: int sz = Math.abs(rand.nextInt()) % MAX_ALLOC_SIZE;
265: int key = Math.abs(rand.nextInt());
266: ht.put(new Integer(key), new byte[sz]);
267: }
268:
269: if (BOTTLENECK_SLEEP) {
270: MDWUtil.sleep(SLEEP_TIME);
271: }
272:
273: if (BOTTLENECK_PROCESSFILE) {
274: try {
275:
276: for (int run = 0; run < (NUM_RUNS * quality); run++) {
277: RandomAccessFile raf = new RandomAccessFile(
278: RANDOM_FILE, "r");
279: for (int i = 0; i < NUM_BYTES_TO_READ; i++) {
280: raf.read(data, 0, NUM_BYTES_TO_READ);
281: // data[i] = (byte)raf.read();
282: }
283: raf.close();
284: for (int n = 0; n < (NUM_SUMS * quality); n++) {
285: for (int i = 0; i < NUM_BYTES_TO_READ; i++) {
286: sum += data[i];
287: }
288: }
289: }
290:
291: } catch (Exception e) {
292: System.err
293: .println("Warning: Bottleneck processing got exception: "
294: + e);
295: }
296: }
297:
298: if (BOTTLENECK_PROCESSRANDOM) {
299: try {
300: Random r = new Random();
301: for (int run = 0; run < NUM_RUNS; run++) {
302: r.nextBytes(data);
303: for (int n = 0; n < NUM_SUMS; n++) {
304: for (int i = 0; i < NUM_BYTES_TO_READ; i++) {
305: sum += data[i];
306: }
307: }
308: }
309:
310: } catch (Exception e) {
311: System.err
312: .println("Warning: Bottleneck processing got exception: "
313: + e);
314: }
315: }
316:
317: }
318:
319: }
|