tl; dr: Функция, которую я написал, создает несколько дочерних процессов, которые разрешают обещание, когда они отправляют свои данные в сообщении. Хотя функция обертывает все эти обещания в Promise.All, функция вернется внезапно, и обещание. Все не разрешает и не отклоняет, хотя все процессы завершаются без ошибок. Есть идеи, почему это происходит?
Чтобы ускорить процесс сбора данных, у меня родительский процесс взял некоторые входные данные (даты, которые нужно запросить в базах данных SQL, если быть точным) и отправьте их на некоторое количество дочерних процессов в кусках равного размера, ожидая окончания работы детей обрабатывая их данные, завершая их результаты в большом обещании.
Хотя это работает для меньших наборов данных, для более крупных, родительское обещание просто вернется в командную строку - не будет разрешать и не отклонять или даже продолжать эту функцию. После просмотра нескольких журналов кажется, что, хотя все дочерние процессы корректно обрабатывают и отправляют свои данные, родитель не получает результаты нескольких (т.е. 2 из 10) процессов. Пропущенные сообщения появляются ближе к концу обработки данных (когда несколько дочерних процессов завершают и отправляют сообщения примерно в одно и то же время)
Сокращенный код:
// main function
function createArray(i,j) // returns an array of i empty arrays, each of length j
function chunkify(a, n, balanced) // divides array a into n chunks (balancing them in size if true) returning an array of chunks
function kidcollector(snaptimes,course) {
var done = 1;
var numchild = 10
const chunked = chunkify(snaptimes,numchild,true);
// array of numchild promises to be resolved upon arrival of data
var collectedPromises = _.times(numchild).map(i => {
return new Promise((resolve, reject) => {
var child = child_process.fork('./child.js');
// send chunk of data to each child
child.send({
times:chunked[i],
c:course
});
child.on('error', (err) => {
console.log('Child error.');
reject(err)
});
child.on('message', function(m) {
if (m.err) {
console.log('Got error from '+ m.child, m.err);
reject(m.err);
} else {
console.log('recieved data from ' + m.child + '! ' + done + ' out of ' + numchild);
done++;
resolve(m.data);
}
});
});
})
return Promise.all(collectedPromises)
.then(results => {
// compile all data into one array then return it
})
.catch(err => {
console.log("One of the kids messed up:", err);
})
};
// child.js, a separate file
const connString = // it a secret!
const client = new Client(connString);
client.connect();
client.on('error', (err) => {
console.error('Client error:', err.stack)
})
process.on('exit', (err) => {
if (err) console.log(process.pid + ' has recieved error:', err);
client.end(() => console.log(process.pid + ' has disconnected on process end', err));
})
process.on('disconnect', (err) => {
if (err) console.log(process.pid + ' has recieved error:', err);
client.end(() => console.log(process.pid + ' has disconnected on process disconnect'));
})
process.on('message', function(m) {
collector(m.times,m.c,process.pid) // async function which compiles data across SQL databases
.then(async function(subdata) {
console.log("all done");
await process.send({
child: process.pid,
data: subdata
});
await process.disconnect();
})
.catch(async function(err) {
console.log("FAILED IN CHILD", err)
await process.send({
child: process.pid,
err: err
});
await process.disconnect();
})
});
Таким образом, после запуска, как ожидалось, в течение некоторого времени, ближе к концу обработки данных, журнал выглядит следующим образом:
all done // child says they're done
recieved data from 5486! 5 out of 10 // parent has received their data
5486 has disconnected on process disconnect // child disconnects
5481 processing snaptime #35 at 2017-07-31T20:26:40.322Z // child is now processing a new time from their given array
all done
recieved data from 5478! 6 out of 10
5478 has disconnected on process disconnect
5483 processing snaptime #34 at 2017-07-31T20:26:51.065Z
5485 processing snaptime #35 at 2017-07-31T20:27:01.876Z
all done // child says they're done
5477 has disconnected on process disconnect // child disconnects, but parent hasn't received data
all done
recieved data from 5481! 7 out of 10 // all good here
5481 has disconnected on process disconnect
5483 processing snaptime #35 at 2017-07-31T20:27:47.834Z
all done
5485 has disconnected on process disconnect // didn't receive message here
all done
recieved data from 5483! 8 out of 10
5483 has disconnected on process disconnect
hansy@Hansys-MacBook-Air ~/Documents/GitHub // and we're at the command line...?
В случае с обещанием.all(), код должен регистрировать время выполнения и при отклонении, он должен регистрировать, что один из детей перепутался, и его ошибка.
Любые идеи относительно того, что происходит и/или как решить эту проблему, тем более, что это происходит только с большими наборами данных? (Я использую узел v8.0.0 с 10 дочерними процессами)
Ваша проблема, кажется, что process.send
не возвращает обещание можно было await
, но вместо этого принимает (дополнительно) обратного вызова. Поэтому ваш вызов disconnect
не будет ждать отправки сообщения.
Ваш родительский процесс просто закончил, когда в очереди не было больше событий, даже если обещание еще не было достигнуто. То, что вы хотите, чтобы слушать это exit
событие дочернего процесса, а не только error
одна. Когда вы reject
от этого, вы убедитесь, что ваш Promise.all
всегда будет решать независимо от того, что происходит с дочерними процессами.
рекомендую
// parent
…
new Promise((resolve, reject) => {
const child = child_process.fork('./child.js');
child.on('error', reject);
child.on('exit', reject);
child.on('message', resolve); // should happen before exit
child.send({
times: chunked[i],
c: course
});
}).then(function(m) {
if (m.err) {
console.log('received error from #${i} (${m.child})', m.err);
throw m.err;
} else {
console.log('received data from #${i} (${m.child})');
return m.data;
}
}, function(err) {
console.log('Got abort from #${i} (${m.child})');
throw err;
});
// child
…
process.on('message', function(m) {
collector(m.times, m.c, process.pid) // async function which compiles data across SQL databases
.then(function(subdata) {
console.log(process.pid+" done");
return {
child: process.pid,
data: subdata
};
}, function(err) {
console.log(process.pid+" FAILED:", err)
return {
child: process.pid,
err: err
};
}).then(function(data) {
return new Promise(function(resolve, reject) {
process.send(data, function(err) {
if (err) reject(err);
else resolve();
});
});
}).catch(function(err) {
console.log(process.pid+" FAILED to send result", err)
}).then(function() {
process.disconnect();
})
});