原标题:如何在javascript中减慢PgSQL结果行流?
我在使用RXJS(5.4.0)和PostgreSQL(驱动程序" pg":" 6.1.4")的nodejs v4.5.0中遇到了内存不足的问题。
我手动创建一个可观察的PgSQL行,如下所示:
return Rx.Observable.create((subscriber) => {
pool.connect().then((client: pg.Client) => {
const stream:any = client.query(query.toParam());
stream.on('row', (row) => {
subscriber.next(row);
});
stream.on('end', () => {
subscriber.complete();
client.release();
});
});
});
然后我将一些运算符附加到rx observable并进行一些处理。 请注意,从数据库返回的行有点重。
调查导致我得出一个结论: 返回数据库中的行的速度要快得多,然后才能进行处理。必须为重型数据保留内存才能等待处理,这会导致内存不足问题:
致命错误:CALL_AND_RETRY_LAST分配失败 - 处理内存不足
中止陷阱:6
我没有在PostgreSQL驱动程序上看到任何选项来暂停流。我有什么想法可以解决这个问题吗?
答案 0 :(得分:5)
如果您使用同一作者的pg-cursor
library,则应该相对简单:
return Rx.Observable.defer(() => pg.connect())
.flatMap(client => {
const cursor = client.query(new Cursor('SELECT * FROM some_table WHERE prop > $1', [100]))
const observableCursor = Rx.Observable.bindNodeCallback(cursor.read.bind(cursor));
// Get the first 100 items
observableCursor(100)
.map(processRows)
// This will only emit after the first one completes
// and will recursively call this for each result
.expand(_ =>
observableCursor(100)
.map(processRows)
)
// Unsubscribes once we don't get any more results
.takeWhile(rows => rows.length > 0)
});
答案 1 :(得分:0)
我试图写@paulpdaniels解决方案建议的尽可能多的可读性,但我不确定这是否真的是反应式。
我还有不同的处理函数,这取决于上下文变量。我喜欢有一个架构,可以轻松地使用不同的rxjs操作符链,这取决于上下文条件并隐藏cursor.read逻辑,但我担心它不可能。
当我明确告诉数据库带来下一个大量数据(cursor.read)时必须有一个地方,并且它必须在我从重数据中释放内存之后的地方。
以下解决方案是打字稿:
const db = require('./libs/db.js');
import * as Cursor from 'pg-cursor';
import * as pg from 'pg';
import * as Rx from 'rxjs';
let cursor, readHandler;
const source = Rx.Observable.create((subscriber) => {
db.getClient().then((client: pg.Client) => {
cursor = client.query(new Cursor('SELECT * FROM some_table WHERE prop > $1', [100]));
readHandler = function (err, rowsBulk) {
if (err) {
subscriber.error(err);
return client.release();
}
if (!rowsBulk.length) {
subscriber.complete();
return client.release();
}
subscriber.next(rowsBulk);
};
}).then(() => {
// fetch first 100 records
cursor.read(100, readHandler);
});
});
source.flatMap((item) => {
return new Promise((resolve) => {
console.log('processing batch of items');
setTimeout(() => { // timeout simulates processing
item = null;
console.log('release memory');
resolve({});
}, 5000);
});
}).map(() => {
// fetch every next 100 records
console.log('allocate new memory');
cursor.read(100, readHandler);
return '.';
}).subscribe(console.log, (err) => {
console.log('Error occurred');
console.error(err);
});