/* Web Polygraph http://www.web-polygraph.org/
* (C) 2003-2006 The Measurement Factory
* Licensed under the Apache License, Version 2.0 */
#include "base/polygraph.h"
#include "xstd/h/iostream.h"
#include "xstd/Clock.h"
#include "xstd/Rnd.h"
#include "xstd/gadgets.h"
#include "base/RndPermut.h"
#include "base/ObjTimes.h"
#include "pgl/PglQualifSym.h"
#include "pgl/ObjLifeCycleSym.h"
#include "csm/ObjLifeCycle.h"
ObjLifeCycle::ObjLifeCycle():
theModTime(0), theExpSelector(0),
theModVar(0), theShowModRatio(0) {
}
ObjLifeCycle::~ObjLifeCycle() {
// do not delete because PGL does not clone distributions
// delete theModTime;
}
void ObjLifeCycle::configure(const ObjLifeCycleSym *cfg) {
Assert(cfg);
if (cfg->bday()) {
cerr << cfg->loc() << "Object Life Cycle birthday"
<< "settings are depricated and ignored" << endl;
}
// move all the checks from 1.3 here
cfg->variance(theModVar);
cfg->withLmt(theShowModRatio);
modTime(cfg->length());
Array<QualifSym*> qs;
cfg->expires(theExpires, theExpSelector, qs);
// set rng
if (theExpSelector)
theExpSelector->rndGen(&theRng);
{for (int i = 0; i < theExpires.count(); ++i) {
if (theExpires[i])
theExpires[i]->rndGen(&theRng);
}}
// interpret qualifiers
for (int i = 0; i < qs.count(); ++i) {
if (qs[i]) {
Assert(qs[i]->kind() != QualifSym::qfNone);
theExpGapTypes.append((ExpGapType)gapType(qs[i]->kind()));
} else
if (theExpires[i]) {
cerr << cfg->loc() << "expire distribution (at position "
<< i+1
<< ") does not have a qualifier (e.g., `lmt' or `now')"
<< endl;
exit(-2);
} else
theExpGapTypes.append(egtNone);
}
}
void ObjLifeCycle::modVar(double aVar) {
theModVar = aVar;
}
void ObjLifeCycle::modTime(RndDistr *aDistr) {
delete theModTime;
theModTime = aDistr;
if (theModTime)
theModTime->rndGen(&theRng);
}
// calculates modification and expiration times for a given object
// note: result times must be rounded to second resolution or
// comparison of HTTP-equal dates may not work!
void ObjLifeCycle::calcTimes(int seed, ObjTimes ×) {
// must have a mod time model to proceed
if (!theModTime) {
times.reset();
return;
}
// note: all distributions are linked to this rng
theRng.seed(seed);
// all time computations are in [sec]
// used a lot
const int now = TheClock.time().sec();
// several "persistent" choices
const int expIdx = theExpSelector ? (int)theExpSelector->trial() : -1;
times.doShowLmt = theRng() < theShowModRatio;
const int cycle = Max(1, (int)theModTime->trial());
const int birth = theRng(0, cycle);
// also persistent: expiration gap (used below)
// the delta is persistent because that's how real servers
// have it configured: mod + const_delta OR now + const_delta
RndDistr *expd = expIdx >= 0 ? theExpires[expIdx] : 0;
const int expGap = !expd ? 0 : (int)expd->trial();
// find min cycleCount (lmt without random, cycle dependent delta)
int cycleCnt = (now - (birth + cycle/2)) / cycle;
// find LMT and NMT so that NMT is in the future
// (do not want to produce stale objects)
int lmt = -1;
int nmt = birth + lmtDelta(seed, cycleCnt, cycle);
do {
lmt = nmt;
nmt = birth + lmtDelta(seed, ++cycleCnt, cycle);
} while (nmt <= now);
times.theLmt = Time::Sec(lmt);
// calculate expiration time if needed
if (expd) {
if (theExpGapTypes[expIdx] == egtNow)
times.theExp = TheClock + Time::Sec(expGap);
else
if (theExpGapTypes[expIdx] == egtNmt)
times.theExp = Time::Sec(nmt + expGap);
else
times.theExp = times.theLmt + Time::Sec(expGap);
} else
times.theExp = Time();
}
// compute lmt delta for a given cycle (with a random component)
// note: cycleCnt == number of full (completed) cycles
int ObjLifeCycle::lmtDelta(int seed, int cycleCnt, int cycleLen) {
if (cycleCnt <= 0)
return 0;
theRng.seed(GlbPermut(cycleCnt, seed));
const double drift = cycleLen/2.0;
return cycleCnt*cycleLen + (int) (theModVar * theRng(-drift, +drift));
}
int ObjLifeCycle::gapType(int qualifSymKind) const {
switch (qualifSymKind) {
case QualifSym::qfLmt:
return egtLmt;
case QualifSym::qfNow:
return egtNow;
case QualifSym::qfNmt:
return egtNmt;
default:
Assert(false);
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1