1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
'use strict';
load('jstests/concurrency/fsm_libs/assert.js');
load('jstests/concurrency/fsm_libs/cluster.js'); // for Cluster.isStandalone
load('jstests/concurrency/fsm_libs/parse_config.js'); // for parseConfig
var workerThread = (function() {
// workloads = list of workload filenames
// args.tid = the thread identifier
// args.data = map of workload -> 'this' parameter passed to the FSM state functions
// args.host = the address to make a new connection to
// args.latch = CountDownLatch instance for starting all threads
// args.dbName = the database name
// args.collName = the collection name
// args.cluster = connection strings for all cluster nodes (see cluster.js for format)
// args.clusterOptions = the configuration of the cluster
// args.seed = seed for the random number generator
// args.globalAssertLevel = the global assertion level to use
// args.errorLatch = CountDownLatch instance that threads count down when they error
// run = callback that takes a map of workloads to their associated $config
function main(workloads, args, run) {
var myDB;
var configs = {};
globalAssertLevel = args.globalAssertLevel;
try {
if (Cluster.isStandalone(args.clusterOptions)) {
myDB = db.getSiblingDB(args.dbName);
} else {
if (typeof db !== 'undefined') {
// The implicit database connection created within the thread's scope
// is unneeded, so forcibly clean it up.
db = null;
gc();
}
myDB = new Mongo(args.host).getDB(args.dbName);
}
workloads.forEach(function(workload) {
load(workload); // for $config
var config = parseConfig($config); // to normalize
// Copy any modifications that were made to $config.data
// during the setup function of the workload (see caveat
// below).
// XXX: Changing the order of extend calls causes problems
// for workloads that reference $super.
// Suppose you have workloads A and B, where workload B extends
// workload A. The $config.data of workload B can define a
// function that closes over the $config object of workload A
// (known as $super to workload B). This reference is lost when
// the config object is serialized to BSON, which results in
// undefined variables in the derived workload.
var data = Object.extend({}, args.data[workload], true);
data = Object.extend(data, config.data, true);
// Object.extend() defines all properties added to the destination object as
// configurable, enumerable, and writable. To prevent workloads from changing
// the iterations and threadCount properties in their state functions, we redefine
// them here as non-configurable, non-enumerable, and non-writable.
Object.defineProperties(data, {
'iterations': {
configurable: false,
enumerable: false,
writable: false,
value: data.iterations
},
'threadCount': {
configurable: false,
enumerable: false,
writable: false,
value: data.threadCount
}
});
data.tid = args.tid;
configs[workload] = {
data: data,
db: myDB,
collName: args.collName,
cluster: args.cluster,
iterations: data.iterations,
passConnectionCache: config.passConnectionCache,
startState: config.startState,
states: config.states,
transitions: config.transitions
};
});
args.latch.countDown();
// Converts any exceptions to a return status. In order for the
// parent thread to call countDown() on our behalf, we must throw
// an exception. Nothing prior to (and including) args.latch.countDown()
// should be wrapped in a try/catch statement.
try {
args.latch.await(); // wait for all threads to start
Random.setRandomSeed(args.seed);
run(configs);
return { ok: 1 };
} catch(e) {
args.errorLatch.countDown();
return {
ok: 0,
err: e.toString(),
stack: e.stack,
tid: args.tid,
workloads: workloads,
};
}
} finally {
// Avoid retention of connection object
configs = null;
myDB = null;
gc();
}
}
return {
main: main
};
})();
|