Skip to content

Commit

Permalink
Add shard client prop fetching, remove guild count fetching, improve …
Browse files Browse the repository at this point in the history
…eval
  • Loading branch information
Gawdl3y committed Sep 24, 2016
1 parent e4f416a commit 1008f60
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 42 deletions.
2 changes: 1 addition & 1 deletion docs/docs.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/client/Client.js
Expand Up @@ -150,6 +150,11 @@ class Client extends EventEmitter {
} catch (err) {
process.send({ _evalError: err });
}
} else if (message._fetchProp) {
const props = message._fetchProp.split('.');
let value = this; // eslint-disable-line consistent-this
for (const prop of props) value = value[prop];
process.send({ _fetchProp: message._fetchProp, _fetchPropValue: value });
}
});
}
Expand Down
41 changes: 40 additions & 1 deletion src/sharding/Shard.js
Expand Up @@ -39,6 +39,9 @@ class Shard {
this.process.once('exit', () => {
if (this.manager.respawn) this.manager.createShard(this.id);
});

this._evals = new Set();
this._fetches = new Set();
}

/**
Expand All @@ -58,28 +61,64 @@ class Shard {
/**
* Evaluates a script on the shard, in the context of the Client.
* @param {string} script JavaScript to run on the shard
* @returns {Promise<*>} Result of the script
* @returns {Promise<*>} Result of the script execution
*/
eval(script) {
if (this._evals.has(script)) return Promise.reject(new Error('Already running an eval for the script.'));
this._evals.add(script);

return new Promise((resolve, reject) => {
const listener = message => {
if (!message) return;
if (message._evalResult) {
this.process.removeListener('message', listener);
this._evals.delete(script);
resolve(message._evalResult);
} else if (message._evalError) {
this.process.removeListener('message', listener);
const err = new Error(message._evalError.message, message._evalError.fileName, message._evalError.lineNumber);
err.name = message._evalError.name;
err.columnNumber = message._evalError.columnNumber;
err.stack = message._evalError.stack;
this._evals.delete(script);
reject(err);
}
};
this.process.on('message', listener);

this.send({ _eval: script }).catch(err => {
this.process.removeListener('message', listener);
this._evals.delete(script);
reject(err);
});
});
}

/**
* Fetches a Client property value of the shard.
* @param {string} prop Name of the Client property to get, using periods for nesting
* @returns {Promise<*>}
* @example
* shard.fetchClientValue('guilds.size').then(count => {
* console.log(`${count} guilds in shard ${shard.id}`);
* }).catch(console.error);
*/
fetchClientValue(prop) {
if (this._fetches.has(prop)) return Promise.reject(new Error(`Already running a fetch for ${prop}.`));
this._fetches.add(prop);

return new Promise((resolve, reject) => {
const listener = message => {
if (typeof message !== 'object' || message._fetchProp !== prop) return;
this.process.removeListener('message', listener);
this._fetches.delete(prop);
resolve(message._fetchPropValue);
};
this.process.on('message', listener);

this.send({ _fetchProp: prop }).catch(err => {
this.process.removeListener('message', listener);
this._fetches.delete(prop);
reject(err);
});
});
Expand Down
52 changes: 12 additions & 40 deletions src/sharding/ShardingManager.js
Expand Up @@ -117,7 +117,7 @@ class ShardingManager extends EventEmitter {
/**
* Evaluates a script on all shards, in the context of the Clients.
* @param {string} script JavaScript to run on each shard
* @returns {Promise<Array>} Results of the script
* @returns {Promise<Array>} Results of the script execution
*/
broadcastEval(script) {
const promises = [];
Expand All @@ -126,48 +126,20 @@ class ShardingManager extends EventEmitter {
}

/**
* Obtains the total guild count across all shards.
* @param {number} [timeout=3000] Time to automatically fail after (in milliseconds)
* @returns {Promise<number>}
* Fetches a Client property value of each shard.
* @param {string} prop Name of the Client property to get, using periods for nesting
* @returns {Promise<Array>}
* @example
* manager.fetchClientValues('guilds.size').then(results => {
* console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`);
* }).catch(console.error);
*/
fetchGuildCount(timeout = 3000) {
fetchClientValues(prop) {
if (this.shards.size === 0) return Promise.reject(new Error('No shards have been spawned.'));
if (this.shards.size !== this.totalShards) return Promise.reject(new Error('Still spawning shards.'));
if (this._guildCountPromise) return this._guildCountPromise;

this._guildCountPromise = new Promise((resolve, reject) => {
this._guildCount = 0;
this._guildCountReplies = 0;

const listener = message => {
if (typeof message !== 'object' || !message._guildCount) return;

this._guildCountReplies++;
this._guildCount += message._guildCount;

if (this._guildCountReplies >= this.shards.size) {
clearTimeout(this._guildCountTimeout);
process.removeListener('message', listener);
this._guildCountTimeout = null;
this._guildCountPromise = null;
resolve(this._guildCount);
}
};
process.on('message', listener);

this._guildCountTimeout = setTimeout(() => {
process.removeListener('message', listener);
this._guildCountPromise = null;
reject(new Error('Took too long to fetch the guild count.'));
}, timeout);

this.broadcast('_guildCount').catch(err => {
process.removeListener('message', listener);
this._guildCountPromise = null;
reject(err);
});
});
return this._guildCountPromise;
const promises = [];
for (const shard of this.shards.values()) promises.push(shard.fetchClientValue(prop));
return Promise.all(promises);
}
}

Expand Down

0 comments on commit 1008f60

Please sign in to comment.