Push scalability (#3080)
* Update status through increment * adds support for incrementing nested keys * fix issue when having spaces in keys for ordering * Refactors PushController to use worker * Adds tests for custom push queue config * Makes PushController adapter independant * Better logging of _PushStatus in VERBOSE
This commit is contained in:
65
src/Adapters/MessageQueue/EventEmitterMQ.js
Normal file
65
src/Adapters/MessageQueue/EventEmitterMQ.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import events from 'events';
|
||||
|
||||
const emitter = new events.EventEmitter();
|
||||
const subscriptions = new Map();
|
||||
|
||||
function unsubscribe(channel: string) {
|
||||
if (!subscriptions.has(channel)) {
|
||||
//console.log('No channel to unsub from');
|
||||
return;
|
||||
}
|
||||
//console.log('unsub ', channel);
|
||||
emitter.removeListener(channel, subscriptions.get(channel));
|
||||
subscriptions.delete(channel);
|
||||
}
|
||||
|
||||
class Publisher {
|
||||
emitter: any;
|
||||
|
||||
constructor(emitter: any) {
|
||||
this.emitter = emitter;
|
||||
}
|
||||
|
||||
publish(channel: string, message: string): void {
|
||||
this.emitter.emit(channel, message);
|
||||
}
|
||||
}
|
||||
|
||||
class Consumer extends events.EventEmitter {
|
||||
emitter: any;
|
||||
|
||||
constructor(emitter: any) {
|
||||
super();
|
||||
this.emitter = emitter;
|
||||
}
|
||||
|
||||
subscribe(channel: string): void {
|
||||
unsubscribe(channel);
|
||||
const handler = (message) => {
|
||||
this.emit('message', channel, message);
|
||||
}
|
||||
subscriptions.set(channel, handler);
|
||||
this.emitter.on(channel, handler);
|
||||
}
|
||||
|
||||
unsubscribe(channel: string): void {
|
||||
unsubscribe(channel);
|
||||
}
|
||||
}
|
||||
|
||||
function createPublisher(): any {
|
||||
return new Publisher(emitter);
|
||||
}
|
||||
|
||||
function createSubscriber(): any {
|
||||
return new Consumer(emitter);
|
||||
}
|
||||
|
||||
const EventEmitterMQ = {
|
||||
createPublisher,
|
||||
createSubscriber
|
||||
}
|
||||
|
||||
export {
|
||||
EventEmitterMQ
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
/*eslint no-unused-vars: "off"*/
|
||||
// Push Adapter
|
||||
//
|
||||
@@ -11,13 +12,15 @@
|
||||
// android push and APNS for ios push.
|
||||
|
||||
export class PushAdapter {
|
||||
send(devices, installations, pushStatus) { }
|
||||
send(body: any, installations: any[], pushStatus: any): ?Promise<*> {}
|
||||
|
||||
/**
|
||||
* Get an array of valid push types.
|
||||
* @returns {Array} An array of valid push types
|
||||
*/
|
||||
getValidPushTypes() {}
|
||||
getValidPushTypes(): string[] {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export default PushAdapter;
|
||||
|
||||
@@ -926,16 +926,34 @@ export class PostgresStorageAdapter {
|
||||
} else if (typeof fieldValue === 'object'
|
||||
&& schema.fields[fieldName]
|
||||
&& schema.fields[fieldName].type === 'Object') {
|
||||
// Gather keys to increment
|
||||
const keysToIncrement = Object.keys(originalUpdate).filter(k => {
|
||||
// choose top level fields that have a delete operation set
|
||||
return originalUpdate[k].__op === 'Increment' && k.split('.').length === 2 && k.split(".")[0] === fieldName;
|
||||
}).map(k => k.split('.')[1]);
|
||||
|
||||
let incrementPatterns = '';
|
||||
if (keysToIncrement.length > 0) {
|
||||
incrementPatterns = ' || ' + keysToIncrement.map((c) => {
|
||||
const amount = fieldValue[c].amount;
|
||||
return `CONCAT('{"${c}":', COALESCE($${index}:name->>'${c}','0')::int + ${amount}, '}')::jsonb`;
|
||||
}).join(' || ');
|
||||
// Strip the keys
|
||||
keysToIncrement.forEach((key) => {
|
||||
delete fieldValue[key];
|
||||
});
|
||||
}
|
||||
|
||||
const keysToDelete = Object.keys(originalUpdate).filter(k => {
|
||||
// choose top level fields that have a delete operation set
|
||||
return originalUpdate[k].__op === 'Delete' && k.split('.').length === 2
|
||||
return originalUpdate[k].__op === 'Delete' && k.split('.').length === 2 && k.split(".")[0] === fieldName;
|
||||
}).map(k => k.split('.')[1]);
|
||||
|
||||
const deletePatterns = keysToDelete.reduce((p, c, i) => {
|
||||
return p + ` - '$${index + 1 + i}:value'`;
|
||||
}, '');
|
||||
|
||||
updatePatterns.push(`$${index}:name = ( COALESCE($${index}:name, '{}'::jsonb) ${deletePatterns} || $${index + 1 + keysToDelete.length}::jsonb )`);
|
||||
updatePatterns.push(`$${index}:name = ( COALESCE($${index}:name, '{}'::jsonb) ${deletePatterns} ${incrementPatterns} || $${index + 1 + keysToDelete.length}::jsonb )`);
|
||||
|
||||
values.push(fieldName, ...keysToDelete, JSON.stringify(fieldValue));
|
||||
index += 2 + keysToDelete.length;
|
||||
|
||||
Reference in New Issue
Block a user