Bureaucracy smoke test and resulting bug fixes
* Added RegistryTester smoke test which randomly creates and destroys agents in batches * Fixed bug where correct agent id was never making it to client * Implemented Bureau.toString (+refactored summarize method) * Fixed nasty bug due to my assumption that SafeSubscriber =~ Subscriber, they are in fact completely different git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5032 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -49,8 +49,12 @@ public abstract class BureauDirector extends BasicDirector
|
||||
}
|
||||
};
|
||||
|
||||
_subscriber = new SafeSubscriber<AgentObject>(agentId, delegator);
|
||||
_subscriber.subscribe(_ctx.getDObjectManager());
|
||||
Log.info("Subscribing to object " + agentId);
|
||||
|
||||
SafeSubscriber<AgentObject> subscriber =
|
||||
new SafeSubscriber<AgentObject>(agentId, delegator);
|
||||
_subscribers.put(agentId, subscriber);
|
||||
subscriber.subscribe(_ctx.getDObjectManager());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,6 +66,7 @@ public abstract class BureauDirector extends BasicDirector
|
||||
agent = _agents.remove(agentId);
|
||||
|
||||
if (agent == null) {
|
||||
Log.warning("Lost an agent, id " + agentId);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
@@ -71,7 +76,13 @@ public abstract class BureauDirector extends BasicDirector
|
||||
Log.warning("Stopping an agent caused an exception");
|
||||
Log.logStackTrace(t);
|
||||
}
|
||||
_subscriber.unsubscribe(_ctx.getDObjectManager());
|
||||
SafeSubscriber<AgentObject> subscriber = _subscribers.remove(agentId);
|
||||
if (subscriber == null) {
|
||||
Log.warning("Lost a subscriber for agent " + agent);
|
||||
}
|
||||
else {
|
||||
subscriber.unsubscribe(_ctx.getDObjectManager());
|
||||
}
|
||||
_bureauService.agentDestroyed(_ctx.getClient(), agentId);
|
||||
}
|
||||
}
|
||||
@@ -82,6 +93,9 @@ public abstract class BureauDirector extends BasicDirector
|
||||
protected synchronized void objectAvailable (AgentObject agentObject)
|
||||
{
|
||||
int oid = agentObject.getOid();
|
||||
|
||||
Log.info("Object " + oid + " now available");
|
||||
|
||||
Agent agent;
|
||||
try {
|
||||
agent = createAgent(agentObject);
|
||||
@@ -151,5 +165,6 @@ public abstract class BureauDirector extends BasicDirector
|
||||
protected BureauContext _ctx;
|
||||
protected BureauService _bureauService;
|
||||
protected IntMap<Agent> _agents = IntMaps.newHashIntMap();
|
||||
protected SafeSubscriber<AgentObject> _subscriber;
|
||||
protected IntMap<SafeSubscriber<AgentObject>> _subscribers =
|
||||
IntMaps.newHashIntMap();
|
||||
}
|
||||
|
||||
@@ -123,12 +123,12 @@ public class BureauRegistry
|
||||
Bureau bureau = _bureaus.get(agent.bureauId);
|
||||
if (bureau != null && bureau.ready()) {
|
||||
|
||||
_omgr.registerObject(agent);
|
||||
|
||||
Log.info("Bureau ready, sending createAgent " +
|
||||
StringUtil.toString(agent));
|
||||
|
||||
BureauSender.createAgent(bureau.clientObj, agent.getOid());
|
||||
// !TODO: is this the right place to register the object?
|
||||
_omgr.registerObject(agent);
|
||||
bureau.agentStates.put(agent, Bureau.STARTED);
|
||||
|
||||
bureau.summarize();
|
||||
@@ -193,7 +193,7 @@ public class BureauRegistry
|
||||
return;
|
||||
}
|
||||
|
||||
Log.warning("Destroying agent " + StringUtil.toString(agent));
|
||||
Log.info("Destroying agent " + StringUtil.toString(agent));
|
||||
|
||||
// transition the agent to a new state and perform the effect of the transition
|
||||
switch (found.state) {
|
||||
@@ -480,35 +480,48 @@ public class BureauRegistry
|
||||
// The states of the various agents allocated to this bureau
|
||||
Map<AgentObject, Integer> agentStates = Maps.newHashMap();
|
||||
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[Bureau id=").append(bureauId).append(", client=");
|
||||
if (clientObj == null) {
|
||||
builder.append("null");
|
||||
}
|
||||
else {
|
||||
builder.append(clientObj.getOid());
|
||||
}
|
||||
builder.append(", process=").append(process);
|
||||
builder.append(", totalAgents=").append(agentStates.size());
|
||||
agentSummary(builder.append(", ")).append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
boolean ready ()
|
||||
{
|
||||
return clientObj != null;
|
||||
}
|
||||
|
||||
int countInState (int state)
|
||||
StringBuilder agentSummary (StringBuilder str)
|
||||
{
|
||||
Integer state1 = state;
|
||||
if (!agentStates.containsValue(state1)) {
|
||||
return 0;
|
||||
int counts[] = {0, 0, 0, 0, 0};
|
||||
for (Map.Entry<AgentObject, Integer> me : agentStates.entrySet()) {
|
||||
counts[me.getValue()]++;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (Map.Entry<AgentObject, Integer> me : agentStates.entrySet()) {
|
||||
if (me.getValue() == state1) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
str.append(counts[PENDING]).append(" pending, ").
|
||||
append(counts[STARTED]).append(" started, ").
|
||||
append(counts[RUNNING]).append(" running, ").
|
||||
append(counts[DESTROYED]).append(" destroyed, ").
|
||||
append(counts[STILL_BORN]).append(" still born");
|
||||
return str;
|
||||
}
|
||||
|
||||
void summarize ()
|
||||
{
|
||||
Log.info("Bureau " + bureauId + " [" +
|
||||
countInState(PENDING) + " pending, " +
|
||||
countInState(STARTED) + " started, " +
|
||||
countInState(RUNNING) + " running, " +
|
||||
countInState(DESTROYED) + " destroyed, " +
|
||||
countInState(STILL_BORN) + " still born]");
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("Bureau ").append(bureauId).append(" [");
|
||||
agentSummary(str).append("]");
|
||||
Log.info(str.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,6 +187,21 @@
|
||||
</java>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="bureau-testregistry" depends="compile"
|
||||
description="Run the bureau test server and tests the registry.">
|
||||
<java fork="true" classname="com.threerings.bureau.server.RegistryTester">
|
||||
<classpath refid="classpath"/>
|
||||
<sysproperty key="maxAgents" value="500"/>
|
||||
<sysproperty key="numBureaus" value="5"/>
|
||||
<sysproperty key="maxOps" value="5"/>
|
||||
<sysproperty key="createChance" value="70"/>
|
||||
<sysproperty key="minDelay" value="500"/>
|
||||
<sysproperty key="maxDelay" value="1500"/>
|
||||
<sysproperty key="seed" value="0"/>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
<!-- NOTE: this target is launched by the bureau-runserver target, modelling the way bureaus -->
|
||||
<!-- work. As such it is not currently useful on its own -->
|
||||
<target name="bureau-runclient" depends="compile"
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.bureau.server;
|
||||
|
||||
import com.samskivert.util.OneLineLogFormatter;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.Collections;
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Uses a TestServer to pound on the BureauRegistry. Sends random sequences of
|
||||
* startAgent and destroyAgent. Most aspects of the randomness are configurable using system
|
||||
* properties.
|
||||
* TODO: add more types of testing
|
||||
* TODO: allow a quitAfter configuration parameter to shut down automatically
|
||||
*/
|
||||
public class RegistryTester
|
||||
{
|
||||
/**
|
||||
* Convenience function for getting an integral property. Also logs the value.
|
||||
*/
|
||||
public static int intProp (String name, int defaultVal)
|
||||
{
|
||||
String val = System.getProperty(name);
|
||||
if (val == null) {
|
||||
TestServer.log.info("Property " + name + " is " + defaultVal);
|
||||
return defaultVal;
|
||||
}
|
||||
int ival = Integer.parseInt(val);
|
||||
TestServer.log.info("Property " + name + " is " + ival);
|
||||
return ival;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new server and runs the registry tester on it.
|
||||
*/
|
||||
public static void main (String[] args)
|
||||
{
|
||||
// make log pretty
|
||||
OneLineLogFormatter.configureDefaultHandler();
|
||||
|
||||
TestServer server = new TestServer();
|
||||
RegistryTester tester = new RegistryTester(server);
|
||||
|
||||
try {
|
||||
server.init();
|
||||
tester.start();
|
||||
server.run();
|
||||
|
||||
} catch (Exception e) {
|
||||
TestServer.log.warning("Unable to initialize server.");
|
||||
TestServer.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new registry tester.
|
||||
*/
|
||||
public RegistryTester (TestServer server)
|
||||
{
|
||||
_server = server;
|
||||
|
||||
_maxAgents = intProp("maxAgents", 500);
|
||||
_numBureaus = intProp("numBureaus", 5);
|
||||
_maxOps = intProp("maxOps", 5);
|
||||
_createChance = intProp("createChance", 70);
|
||||
_minDelay = intProp("minDelay", 500);
|
||||
_maxDelay = intProp("maxDelay", 2000);
|
||||
|
||||
// stop the tests when the server shuts down
|
||||
// TODO: this is not called on Ctrl-C, need a way to shut down gracefully
|
||||
_server.registerShutdowner(new TestServer.Shutdowner() {
|
||||
public void shutdown () {
|
||||
TestServer.log.info("Shutting down tests");
|
||||
_stop = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the test thread.
|
||||
*/
|
||||
public void start ()
|
||||
{
|
||||
Thread thread = new Thread("Registry test thread") {
|
||||
public void run () {
|
||||
TestServer.log.info(getName() + " started");
|
||||
runTestThread();
|
||||
TestServer.log.info(getName() + " stopped");
|
||||
}
|
||||
};
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the test thread.
|
||||
*/
|
||||
public void stop ()
|
||||
{
|
||||
_stop = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main loop for the tests.
|
||||
*/
|
||||
protected void runTestThread ()
|
||||
{
|
||||
// set up the rngs (2 are needed since one is called from the dobj thread)
|
||||
long seed = Long.parseLong(System.getProperty("seed", "0"));
|
||||
if (seed == 0) {
|
||||
seed = System.currentTimeMillis();
|
||||
}
|
||||
_rng1 = new Random(seed);
|
||||
_rng2 = new Random(seed);
|
||||
|
||||
TestServer.log.info("Running tests, seed is " + seed);
|
||||
|
||||
// runnable that generates N requests to create or destroy agents
|
||||
Runnable createOrDestroyAgents = new Runnable() {
|
||||
public void run () {
|
||||
int ops = 1 + _rng1.nextInt(_maxOps);
|
||||
TestServer.log.info("Starting " + ops + " agent requests");
|
||||
for (int i = 0; i < ops; ++i) {
|
||||
randomlyCreateOrDestroyAgent();
|
||||
}
|
||||
TestServer.log.info("Finished " + ops + " agent requests");
|
||||
}
|
||||
};
|
||||
|
||||
// the main test loop
|
||||
while (!_stop) {
|
||||
try {
|
||||
// sleep for a bit
|
||||
int sleep = _maxDelay - _minDelay + 1;
|
||||
sleep = _minDelay + _rng2.nextInt(sleep);
|
||||
Thread.sleep(sleep);
|
||||
}
|
||||
catch (InterruptedException ie) {
|
||||
break;
|
||||
}
|
||||
|
||||
// create or destroy some agents
|
||||
_server.omgr.postRunnable(createOrDestroyAgents);
|
||||
}
|
||||
|
||||
// clean up
|
||||
_server.omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
for (AgentObject obj : _agents) {
|
||||
_server.breg.destroyAgent(obj);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Does what the name says using the configuration values.
|
||||
*/
|
||||
protected void randomlyCreateOrDestroyAgent ()
|
||||
{
|
||||
int size = _agents.size();
|
||||
if (size >= _maxAgents ||
|
||||
(size != 0 && _rng1.nextInt(100) >= _createChance)) {
|
||||
AgentObject toRemove = _agents.remove(_rng1.nextInt(size));
|
||||
TestServer.log.info("Removing agent " + toRemove.getOid());
|
||||
_server.breg.destroyAgent(toRemove);
|
||||
}
|
||||
else {
|
||||
AgentObject added = create(_rng1.nextInt(_numBureaus) + 1);
|
||||
_agents.add(added);
|
||||
TestServer.log.info("Added agent " + added.getOid());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new agent for the given bureau.
|
||||
*/
|
||||
protected AgentObject create (int bureau)
|
||||
{
|
||||
AgentObject obj = new AgentObject();
|
||||
obj.bureauType = "test";
|
||||
obj.bureauId = "test-" + bureau;
|
||||
_server.breg.startAgent(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected TestServer _server;
|
||||
protected boolean _stop;
|
||||
protected Random _rng1;
|
||||
protected Random _rng2;
|
||||
protected List<AgentObject> _agents = Lists.newArrayList();
|
||||
|
||||
// maximum number of agents to keep around
|
||||
protected int _maxAgents;
|
||||
|
||||
// number of bureaus to select between
|
||||
protected int _numBureaus;
|
||||
|
||||
// number of operations to perform in one dobj queue task
|
||||
protected int _maxOps;
|
||||
|
||||
// chance of creating a new agent if limit has not been reached
|
||||
protected int _createChance;
|
||||
|
||||
// minimum delay between batches of requests
|
||||
protected int _minDelay;
|
||||
|
||||
// maximum delay between batches of requests
|
||||
protected int _maxDelay;
|
||||
}
|
||||
@@ -21,24 +21,35 @@
|
||||
|
||||
package com.threerings.bureau.server;
|
||||
|
||||
import com.threerings.bureau.Log;
|
||||
import com.threerings.bureau.data.AgentObject;
|
||||
import com.threerings.presents.server.PresentsServer;
|
||||
import com.samskivert.util.OneLineLogFormatter;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Extends a presents server to include a bureau registry.
|
||||
*/
|
||||
public class TestServer extends PresentsServer
|
||||
{
|
||||
/** We dispatch our log messages through this logger. */
|
||||
public static Logger log = Logger.getLogger(TestServer.class.getName());
|
||||
|
||||
/**
|
||||
* The bureau registry for the server. Will be null until <code>init</code> is called.
|
||||
*/
|
||||
public static BureauRegistry breg;
|
||||
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.log(Level.WARNING, t.getMessage(), t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new server and runs it.
|
||||
*/
|
||||
@@ -50,21 +61,11 @@ public class TestServer extends PresentsServer
|
||||
final TestServer server = new TestServer();
|
||||
try {
|
||||
server.init();
|
||||
|
||||
// request a new agent
|
||||
// TODO: more tests here - create a thread that posts different kinds of requests
|
||||
// and somehow monitors results from client
|
||||
server.omgr.postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
server.createTestAgent();
|
||||
}
|
||||
});
|
||||
|
||||
server.run();
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Unable to initialize server.");
|
||||
Log.logStackTrace(e);
|
||||
log.warning("Unable to initialize server.");
|
||||
logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,15 +94,4 @@ public class TestServer extends PresentsServer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the bureau client create an agent.
|
||||
*/
|
||||
protected void createTestAgent ()
|
||||
{
|
||||
AgentObject obj = new AgentObject();
|
||||
obj.bureauType = "test";
|
||||
obj.bureauId = "test";
|
||||
breg.startAgent(obj);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user