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);
|
Log.info("Subscribing to object " + agentId);
|
||||||
_subscriber.subscribe(_ctx.getDObjectManager());
|
|
||||||
|
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);
|
agent = _agents.remove(agentId);
|
||||||
|
|
||||||
if (agent == null) {
|
if (agent == null) {
|
||||||
|
Log.warning("Lost an agent, id " + agentId);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
@@ -71,7 +76,13 @@ public abstract class BureauDirector extends BasicDirector
|
|||||||
Log.warning("Stopping an agent caused an exception");
|
Log.warning("Stopping an agent caused an exception");
|
||||||
Log.logStackTrace(t);
|
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);
|
_bureauService.agentDestroyed(_ctx.getClient(), agentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,6 +93,9 @@ public abstract class BureauDirector extends BasicDirector
|
|||||||
protected synchronized void objectAvailable (AgentObject agentObject)
|
protected synchronized void objectAvailable (AgentObject agentObject)
|
||||||
{
|
{
|
||||||
int oid = agentObject.getOid();
|
int oid = agentObject.getOid();
|
||||||
|
|
||||||
|
Log.info("Object " + oid + " now available");
|
||||||
|
|
||||||
Agent agent;
|
Agent agent;
|
||||||
try {
|
try {
|
||||||
agent = createAgent(agentObject);
|
agent = createAgent(agentObject);
|
||||||
@@ -151,5 +165,6 @@ public abstract class BureauDirector extends BasicDirector
|
|||||||
protected BureauContext _ctx;
|
protected BureauContext _ctx;
|
||||||
protected BureauService _bureauService;
|
protected BureauService _bureauService;
|
||||||
protected IntMap<Agent> _agents = IntMaps.newHashIntMap();
|
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);
|
Bureau bureau = _bureaus.get(agent.bureauId);
|
||||||
if (bureau != null && bureau.ready()) {
|
if (bureau != null && bureau.ready()) {
|
||||||
|
|
||||||
|
_omgr.registerObject(agent);
|
||||||
|
|
||||||
Log.info("Bureau ready, sending createAgent " +
|
Log.info("Bureau ready, sending createAgent " +
|
||||||
StringUtil.toString(agent));
|
StringUtil.toString(agent));
|
||||||
|
|
||||||
BureauSender.createAgent(bureau.clientObj, agent.getOid());
|
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.agentStates.put(agent, Bureau.STARTED);
|
||||||
|
|
||||||
bureau.summarize();
|
bureau.summarize();
|
||||||
@@ -193,7 +193,7 @@ public class BureauRegistry
|
|||||||
return;
|
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
|
// transition the agent to a new state and perform the effect of the transition
|
||||||
switch (found.state) {
|
switch (found.state) {
|
||||||
@@ -480,35 +480,48 @@ public class BureauRegistry
|
|||||||
// The states of the various agents allocated to this bureau
|
// The states of the various agents allocated to this bureau
|
||||||
Map<AgentObject, Integer> agentStates = Maps.newHashMap();
|
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 ()
|
boolean ready ()
|
||||||
{
|
{
|
||||||
return clientObj != null;
|
return clientObj != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int countInState (int state)
|
StringBuilder agentSummary (StringBuilder str)
|
||||||
{
|
{
|
||||||
Integer state1 = state;
|
int counts[] = {0, 0, 0, 0, 0};
|
||||||
if (!agentStates.containsValue(state1)) {
|
for (Map.Entry<AgentObject, Integer> me : agentStates.entrySet()) {
|
||||||
return 0;
|
counts[me.getValue()]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = 0;
|
str.append(counts[PENDING]).append(" pending, ").
|
||||||
for (Map.Entry<AgentObject, Integer> me : agentStates.entrySet()) {
|
append(counts[STARTED]).append(" started, ").
|
||||||
if (me.getValue() == state1) {
|
append(counts[RUNNING]).append(" running, ").
|
||||||
++count;
|
append(counts[DESTROYED]).append(" destroyed, ").
|
||||||
}
|
append(counts[STILL_BORN]).append(" still born");
|
||||||
}
|
return str;
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void summarize ()
|
void summarize ()
|
||||||
{
|
{
|
||||||
Log.info("Bureau " + bureauId + " [" +
|
StringBuilder str = new StringBuilder();
|
||||||
countInState(PENDING) + " pending, " +
|
str.append("Bureau ").append(bureauId).append(" [");
|
||||||
countInState(STARTED) + " started, " +
|
agentSummary(str).append("]");
|
||||||
countInState(RUNNING) + " running, " +
|
Log.info(str.toString());
|
||||||
countInState(DESTROYED) + " destroyed, " +
|
|
||||||
countInState(STILL_BORN) + " still born]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,6 +187,21 @@
|
|||||||
</java>
|
</java>
|
||||||
</target>
|
</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 -->
|
<!-- 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 -->
|
<!-- work. As such it is not currently useful on its own -->
|
||||||
<target name="bureau-runclient" depends="compile"
|
<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;
|
package com.threerings.bureau.server;
|
||||||
|
|
||||||
import com.threerings.bureau.Log;
|
|
||||||
import com.threerings.bureau.data.AgentObject;
|
import com.threerings.bureau.data.AgentObject;
|
||||||
import com.threerings.presents.server.PresentsServer;
|
import com.threerings.presents.server.PresentsServer;
|
||||||
import com.samskivert.util.OneLineLogFormatter;
|
import com.samskivert.util.OneLineLogFormatter;
|
||||||
import com.samskivert.util.StringUtil;
|
import com.samskivert.util.StringUtil;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import java.io.File;
|
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.
|
* Extends a presents server to include a bureau registry.
|
||||||
*/
|
*/
|
||||||
public class TestServer extends PresentsServer
|
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.
|
* The bureau registry for the server. Will be null until <code>init</code> is called.
|
||||||
*/
|
*/
|
||||||
public static BureauRegistry breg;
|
public static BureauRegistry breg;
|
||||||
|
|
||||||
|
public static void logStackTrace (Throwable t)
|
||||||
|
{
|
||||||
|
log.log(Level.WARNING, t.getMessage(), t);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new server and runs it.
|
* Creates a new server and runs it.
|
||||||
*/
|
*/
|
||||||
@@ -50,21 +61,11 @@ public class TestServer extends PresentsServer
|
|||||||
final TestServer server = new TestServer();
|
final TestServer server = new TestServer();
|
||||||
try {
|
try {
|
||||||
server.init();
|
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();
|
server.run();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.warning("Unable to initialize server.");
|
log.warning("Unable to initialize server.");
|
||||||
Log.logStackTrace(e);
|
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