БД блокировки первой транзакции (SAVEPOINT и соединение потеряно) - PullRequest
0 голосов
/ 11 июля 2019

Я могу сократить свой пост при необходимости

Я использую транзакцию pg-обещание для вставки «устройства» и всей его части (например, системы, диска, ...). Транзакция работает ... но только в первый раз . После этого мой сервер больше не может взаимодействовать с БД (ни вставить, ни выбрать).

Вот вывод pg-monitor с этими шагами

Thu Jul 11 2019 14:26:57 GMT+0200 (GMT+02:00) : server is listening on 9000
14:27:11 connect(hidden@hidden); useCount: 0
14:27:11 insert into "public"."roles"("name") values('Test') RETURNING *
14:27:11 disconnect(hidden@hidden)
14:27:15 connect(hidden@hidden); useCount: 1
14:27:15 insert into "public"."roles"("name") values('Test2') RETURNING *
14:27:15 disconnect(hidden@hidden)
14:27:18 connect(hidden@hidden); useCount: 2
14:27:18 tx(Insert-New-Device)/start
14:27:18 tx(Insert-New-Device): begin
14:27:18 tx(Insert-New-Device): insert into "public"."devices"("smanufacturer") values('HP') RETURNING *
14:27:18 tx(Insert-New-Device): insert into "public"."systems"("deviceid","distributionid","archid","smanufacturer") values(15,3,2,'Microsoft Corporation') RETURNING *
14:27:18 tx(Insert-New-Device): commit
14:27:18 tx(Insert-New-Device)/end; duration: .046, success: true
14:27:18 disconnect(hidden@hidden)
14:27:20 connect(hidden@hidden); useCount: 3
14:27:20 tx(Insert-New-Device)/start
14:27:20 tx(Insert-New-Device): savepoint level_1
14:27:20 error: SAVEPOINT can only be used in transaction blocks
         tx(Insert-New-Device): savepoint level_1
14:27:20 tx(Insert-New-Device)/end; duration: .011, success: false
14:27:20 disconnect(hidden@hidden)

ошибка

  1. devices.add бросок

    Ошибка: SAVEPOINT может использоваться только в блоках транзакций

  2. roles.add бросок

    Ошибка: запрос об освобожденном или потерянном соединении

РЕДАКТИРОВАТЬ: обнаружена проблема

Проблема в моих репозиториях. В pg-promise-demo каждое хранилище экспортирует классы, поэтому инициализация БД использует ключевое слово new в событии расширения для их создания. Мои репо не классы. Я пытался преобразовать их в классы, и это работает

До (не работает)

. / Дб / репо / devices.js

'use strict';

var Database = null, pgp = null, Collections = null;

async function add(params) {
  return Database.tx('Insert-New-Device', async t => {
    let system = null;

    const query = pgp.helpers.insert(params.data.device, Collections.insert) + " RETURNING *";
    let device = await t.one(query);

    // if a system is present, insert with diviceId and return
    if(params.data.system) {
      params.data.system.deviceid = device.deviceid;
      system = await t.systems.InsertOne(params);
    }

    return {device, system};
  })
  .catch(ex => {
    throw ex;
  });
}

function createColumnsets() { /* hidden for brevity */ }

// rpc methods
const expose = {
  'devices.insert': add
}

const DevicesRepository = {
  expose,        // expose methods as "rpc methods"
  InsertOne: add // internal use (by another repo for example : Database.devices.InsertOne())
};

module.exports = (db, pgpLib) => {
  Database = db;
  pgp = pgpLib;
  Collections = createColumnsets();

  return DevicesRepository;
}

. / Дб / index.js.js

'use strict';

const promise = require('bluebird');

const repos = {
  Roles: require('./repos/roles'),
  Systems: require('./repos/systems'),
  Devices: require('./repos/devices')
}
const config = require('./conf');

const initOptions = {
    promiseLib: promise,
    extend(obj, dc) {
        obj.roles = repos.Roles(obj, pgp);
        obj.systems = repos.Systems(obj, pgp);
        obj.devices = repos.Devices(obj, pgp);
    }
};

const pgp = require('pg-promise')(initOptions);
const monitor = require('pg-monitor');
monitor.attach(initOptions);
const db = pgp(config);

const methods = Object.assign({}, db.roles.expose, db.systems.expose, db.devices.expose );

module.exports = {
  methods
}

Сейчас (работает без ошибок)

devices.js

'use strict';

class RolesRepository {
  constructor(db, pgp) {
    this.Database = db;
    this.pgp = pgp;

    this.Collections = createColumnsets(pgp);

    this.expose = {
      'roles.insert': this.InsertOne.bind(this)
    }
  }

  makeInsertQuery(role) {
    return this.pgp.helpers.insert(role, this.Collections.insert);
  }

  async InsertOne(params) {
    let query = this.makeInsertQuery(params.data);
    if(params.return) query += " RETURNING *";

    return this.Database.any(query)
                    .then(data => { return data; })
                    .catch(ex => { throw ex; });
  }
}

function createColumnsets(pgp) { /* hidden for brevity */ }

module.exports = RolesRepository

. / Дб / index.js

'use strict';
const promise = require('bluebird');

//const repos = require('./repos'); // ./repos/index.js
const repos = {
  Roles: require('./roles'),
  Systems: require('./systems'),
  Devices: require('./devices'),
};
const config = { /* hidden */ };

const initOptions = {
    promiseLib: promise,
    extend(obj, dc) {
        obj.roles = new repos.Roles(obj, pgp);
        obj.systems = new repos.Systems(obj, pgp);
        obj.devices = new repos.Devices(obj, pgp);
    }
};

const pgp = require('pg-promise')(initOptions);
const monitor = require('pg-monitor');
monitor.attach(initOptions);

const db = pgp(config);

// expose db methods as rpc call
const methods = Object.assign({},
    db.roles.expose,
    db.systems.expose,
    db.devices.expose,
);

module.exports = {
  methods
}

1 Ответ

0 голосов
/ 11 июля 2019

Я не верю, что то, что вы показываете, является полным кодом, потому что тип проблемы, с которой вы столкнулись, невозможен в пределах уровня транзакции pg-обещание .Он не может выполнить SAVEPOINT вне транзакции.

Однако существует один способ взломать его, который может сломать библиотеку таким образом, и я очень подозреваю, что это то, что вы сделали, каким-то образом ...

Когда мы выполняем задачу с методом task или транзакцию с помощью метода tx, метод создает временный контекст соединения, который он дает вам в качестве параметра обратного вызова для выполнения запросов.

Это делается таким образом, чтобы после завершения обратного вызова контекст автоматически уничтожался.И если каким-то образом вы выставляете этот контекст вне функции обратного вызова и начинаете выполнять запросы к нему, вы нарушаете логику контекста соединения.

Один из способов, которым вы можете разорвать его, - это выполнение асинхронной функции, которая использует контекст,и не заканчивать его к тому времени, как обратный вызов завершен.Тогда вы можете иметь этот тип ошибки - Querying against a released or lost connection, который сообщает вам, что контекст исчез / освобожден, и вы все еще пытаетесь выполнить запросы к нему.

Вы должны убедиться, что вы этого не делаетеиспользуйте контекст соединения вне функции обратного вызова, так как он там не применим, и результат его использования может быть непредсказуемым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...