Миграция Knex: запрос транзакции уже выполнен - PullRequest
1 голос
/ 05 января 2020

Использование knex Я хочу добавить 2 дополнительных столбца в существующую таблицу. К уже существующим записям я хотел бы добавить значение на основе расчета. Ниже мой файл миграции. Ошибка во 2-й строке: Cannot read property 'resolve' of undefined.

exports.up = function (knex, Promise) {
  return Promise.resolve()
    .then(function(){
            ... cut to shorten question...
    })
};

Обновление: Я удалил Promise и теперь у меня есть код миграции, приведенный ниже. Это по-прежнему приводит к двум ошибкам, вероятно, связанным с forEach l oop, и к тому, что он не ждет, пока одна часть l oop не закончит sh, прежде чем перейти к следующей части (но я не не знаю, как еще сделать l oop):

необработанное отклонение MigrationLocked: таблица миграции уже заблокирована

запрос транзакции уже завершен, запустите с DEBUG = knex: tx для получения дополнительной информации

const Coupon = require('../../models/coupon');
const Plan = require('../../models/plan');

exports.up = function (knex) {
    return knex.schema.table('transactions', (table) => {
      table.decimal('plan_price', 10, 2);
      table.decimal('discount', 10, 2).defaultTo(0);
    })

    .then(function(return_value){
      knex.select().from('transactions')
      .then((transactions) => {
        transactions.forEach(async function(trans){
          let original_price;
          let total_coupon_discount = 0;

          const plan = await Plan.where({id: trans.plan_id}).fetch();
          if (plan) { original_price = plan.get("price") };

          if (trans.coupon_id) {
            const coupon = await Coupon.where({id: trans.coupon_id}).fetch();
            if (coupon) {
              total_coupon_discount = coupon.get("discount_amount");
              original_price = trans.amount_ex_vat + couponAmount;
            }
          }

          const promise = await knex('transactions')
          .where('id', '=', trans.id)
          .update({
            plan_price: original_price,
            discount: total_coupon_discount
          }).catch(function(error){
            console.log(error)
          }).then(function(){
            console.log('Added data to transaction record');
          })
        })
      })

      return return_value;
    })
};

exports.down = function (knex) {
  return knex.schema.table('transactions', (table) => {
    table.dropColumn('plan_price');
    table.dropColumn('discount');
  });
};

Обновление 2: Предлагаемый синтаксис миграции в ответе @Rich Churcher сделал миграцию эффективной. Но сообщение об ошибке о MigrationLock осталось. Здесь обсуждается аналогичная проблема. Предлагается удалить блокировки из migration_table, но даже полное опустошение этой таблицы для меня не имеет значения.

Таким образом, я добавил DEBUG=knex:* в variables.env, как предполагает этот сайт. Когда я запускаю миграцию, я получаю вывод ниже. Любая идея, что может быть причиной ошибки и как ее решить?

Using environment: development
  knex:client acquired connection from pool: __knexUid1 +0ms
  knex:query select * from information_schema.tables where table_name = ? and table_schema = database() undefined +0ms
  knex:bindings [ 'migrations' ] undefined +0ms
  knex:client releasing connection to pool: __knexUid1 +40ms
  knex:client acquired connection from pool: __knexUid1 +2ms
  knex:query select * from information_schema.tables where table_name = ? and table_schema = database() undefined +40ms
  knex:bindings [ 'migrations_lock' ] undefined +39ms
  knex:client releasing connection to pool: __knexUid1 +5ms
  knex:client acquired connection from pool: __knexUid1 +2ms
  knex:query select * from `migrations_lock` undefined +9ms
  knex:bindings [] undefined +10ms
  knex:client releasing connection to pool: __knexUid1 +22ms
  knex:client acquired connection from pool: __knexUid1 +22ms
  knex:query select `name` from `migrations` order by `id` asc undefined +60ms
  knex:bindings [] undefined +62ms
  knex:client releasing connection to pool: __knexUid1 +25ms
  knex:tx trx2: Starting top level transaction +0ms
  knex:client acquired connection from pool: __knexUid1 +329ms
  knex:query BEGIN; trx2 +348ms
  knex:bindings undefined trx2 +347ms
  knex:query update `migrations_lock` set `is_locked` = ? where `is_locked` = ? trx2 +18ms
  knex:bindings [ 1, 0 ] trx2 +25ms
  knex:client acquired connection from pool: __knexUid3 +47ms
  knex:query select * from information_schema.tables where table_name = ? and table_schema = database() undefined +17ms
  knex:bindings [ 'migrations' ] undefined +8ms
  knex:query select * from information_schema.tables where table_name = ? and table_schema = database() trx2 +6ms
  knex:bindings [ 'migrations' ] trx2 +6ms
  knex:client releasing connection to pool: __knexUid3 +13ms
  knex:client acquired connection from pool: __knexUid3 +18ms
  knex:query select * from information_schema.tables where table_name = ? and table_schema = database() undefined +26ms
  knex:bindings [ 'migrations_lock' ] undefined +26ms
  knex:query select * from information_schema.tables where table_name = ? and table_schema = database() trx2 +2ms
  knex:bindings [ 'migrations_lock' ] trx2 +3ms
  knex:client releasing connection to pool: __knexUid3 +6ms
  knex:client acquired connection from pool: __knexUid3 +1ms
  knex:query select * from `migrations_lock` undefined +4ms
  knex:bindings [] undefined +3ms
  knex:query select * from `migrations_lock` trx2 +2ms
  knex:bindings [] trx2 +18ms
  knex:client releasing connection to pool: __knexUid3 +22ms
  knex:client acquired connection from pool: __knexUid3 +23ms
  knex:query select `name` from `migrations` order by `id` asc undefined +66ms
  knex:bindings [] undefined +51ms
  knex:query select `name` from `migrations` order by `id` asc trx2 +7ms
  knex:bindings [] trx2 +26ms
  knex:client releasing connection to pool: __knexUid3 +55ms
  knex:tx trx4: Starting top level transaction +193ms
  knex:client acquired connection from pool: __knexUid3 +3ms
  knex:query BEGIN; trx4 +27ms
  knex:bindings undefined trx4 +8ms
  knex:query select max(`batch`) as `max_batch` from `migrations` trx2 +23ms
  knex:bindings [] trx2 +43ms
  knex:query update `migrations_lock` set `is_locked` = ? where `is_locked` = ? trx4 +25ms
  knex:bindings [ 1, 0 ] trx4 +5ms
  knex:query alter table `transactions` add `plan_price` decimal(10, 2), add `discount` decimal(10, 2) default '0' trx2 +13ms
  knex:bindings [] trx2 +20ms
Can't take lock to run migrations: Migration table is already locked
If you are sure migrations are not running you can release the lock manually by deleting all the rows = require(migrations lock table: migrations_lock
  knex:query ROLLBACK trx4 +71ms
  knex:bindings undefined trx4 +64ms
  knex:tx trx4: releasing connection +136ms
  knex:client releasing connection to pool: __knexUid3 +180ms
Unhandled rejection MigrationLocked: Migration table is already locked
    (No stack trace)
From previous event:
    at Migrator._getLock (C:\Users\Xxx\node_modules\knex\lib\migrate\Migrator.js:328:13)
    at Migrator._runBatch (C:\Users\Xxx\node_modules\knex\lib\migrate\Migrator.js:343:12)
    at knex.transaction (C:\Users\Xxx\node_modules\knex\lib\migrate\Migrator.js:92:25)
    at init.then.then (C:\Users\Xxx\node_modules\knex\lib\transaction.js:91:24)
    at runCallback (timers.js:705:18)
    at tryOnImmediate (timers.js:676:5)
    at processImmediate (timers.js:658:5)
From previous event:
    at Transaction._promise.Bluebird.using (C:\Users\Xxx\node_modules\knex\lib\transaction.js:77:12)
    at runCallback (timers.js:705:18)
From previous event:
    at new Transaction (C:\Users\Xxx\node_modules\knex\lib\transaction.js:57:30)
    at new Transaction_MySQL (C:\Users\Xxx\node_modules\knex\lib\dialects\mysql\transaction.js:7:1)
    at Client_MySQL.transaction (C:\Users\Xxx\node_modules\knex\lib\dialects\mysql\index.js:52:12)
    at Function.transaction (C:\Users\Xxx\node_modules\knex\lib\util\make-knex.js:40:31)
    at migrationListResolver.listAllAndCompleted.then.then (C:\Users\Xxx\node_modules\knex\lib\migrate\Migrator.js:91:28)
From previous event:
    at Migrator.latest (C:\Users\Xxx\node_modules\knex\lib\migrate\Migrator.js:72:8)
    at Command.commander.command.description.option.action (C:\Users\Xxx\node_modules\knex\bin\cli.js:185:18)
    at Command.listener (C:\Users\Xxx\node_modules\knex\node_modules\commander\index.js:360:8)
    at Command.emit (events.js:189:13)
    at Command.parseArgs (C:\Users\Xxx\node_modules\knex\node_modules\commander\index.js:799:12)
    at Command.parse (C:\Users\Xxx\node_modules\knex\node_modules\commander\index.js:563:21)
    at Liftoff.invoke (C:\Users\Xxx\node_modules\knex\bin\cli.js:344:13)
    at Liftoff.execute (C:\Users\Xxx\node_modules\liftoff\index.js:201:12)
    at module.exports (C:\Users\Xxx\node_modules\flagged-respawn\index.js:51:3)
    at Liftoff.<anonymous> (C:\Users\Xxx\node_modules\liftoff\index.js:191:5)
    at C:\Users\Xxx\node_modules\liftoff\index.js:149:9
    at C:\Users\Xxx\node_modules\v8flags\index.js:138:14
    at C:\Users\Xxx\node_modules\v8flags\index.js:41:14
    at C:\Users\Xxx\node_modules\v8flags\index.js:53:7
    at process._tickCallback (internal/process/next_tick.js:61:11)

  knex:query select * from `transactions` trx2 +185ms
  knex:bindings [] trx2 +227ms
  knex:client acquired connection from pool: __knexUid3 +201ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +88ms
  knex:bindings [ 7, 1 ] trx4 +73ms
  knex:client releasing connection to pool: __knexUid3 +158ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +137ms
  knex:bindings [ 1725, 0, 1 ] trx2 +109ms
  • Added original price and discount to transaction: 1
  knex:client acquired connection from pool: __knexUid3 +35ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +33ms
  knex:bindings [ 2, 1 ] trx4 +33ms
  knex:client releasing connection to pool: __knexUid3 +2ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +3ms
  knex:bindings [ 195, 0, 2 ] trx2 +4ms
  • Added original price and discount to transaction: 2
  knex:client acquired connection from pool: __knexUid3 +51ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +50ms
  knex:bindings [ 6, 1 ] trx4 +49ms
  knex:client releasing connection to pool: __knexUid3 +3ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +4ms
  knex:bindings [ 975, 0, 3 ] trx2 +4ms
  • Added original price and discount to transaction: 3
  knex:client acquired connection from pool: __knexUid3 +36ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +35ms
  knex:bindings [ 5, 1 ] trx4 +35ms
  knex:client releasing connection to pool: __knexUid3 +10ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +31ms
  knex:bindings [ 650, 0, 4 ] trx2 +31ms
  • Added original price and discount to transaction: 4
  knex:client acquired connection from pool: __knexUid3 +56ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +34ms
  knex:bindings [ 5, 1 ] trx4 +34ms
  knex:client releasing connection to pool: __knexUid3 +2ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +9ms
  knex:bindings [ 650, 0, 5 ] trx2 +31ms
  • Added original price and discount to transaction: 5
  knex:client acquired connection from pool: __knexUid3 +62ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +55ms
  knex:bindings [ 6, 1 ] trx4 +34ms
  knex:client releasing connection to pool: __knexUid3 +4ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +6ms
  knex:bindings [ 975, 0, 6 ] trx2 +29ms
  • Added original price and discount to transaction: 6
  knex:client acquired connection from pool: __knexUid3 +64ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +63ms
  knex:bindings [ 6, 1 ] trx4 +39ms
  knex:client releasing connection to pool: __knexUid3 +2ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +4ms
  knex:bindings [ 975, 0, 7 ] trx2 +34ms
  • Added original price and discount to transaction: 7
  knex:client acquired connection from pool: __knexUid3 +66ms
  knex:query select `plans`.* from `plans` where `id` = ? limit ? trx4 +64ms
  knex:bindings [ 5, 1 ] trx4 +34ms
  knex:client releasing connection to pool: __knexUid3 +3ms
  knex:query update `transactions` set `plan_price` = ?, `discount` = ? where `id` = ? trx2 +29ms
  knex:bindings [ 650, 0, 8 ] trx2 +30ms
  • Added original price and discount to transaction: 8
  knex:query insert into `migrations` (`batch`, `migration_time`, `name`) values (?, ?, ?) trx2 +36ms
  knex:bindings [ 4,
  knex:bindings   2020-01-09T21:13:03.923Z,
  knex:bindings   '20200105152452_add_plan_price_and_discount_to_transactions.js' ] trx2 +35ms
  knex:query update `migrations_lock` set `is_locked` = ? trx2 +24ms
  knex:bindings [ 0 ] trx2 +37ms
  knex:query COMMIT; trx2 +42ms
  knex:bindings undefined trx2 +44ms
  knex:tx trx2: releasing connection +945ms
  knex:client releasing connection to pool: __knexUid1 +146ms
Batch 4 run: 1 migrations

knex -V возвращает: «Версия Knex CLI: 0.20.2» и «Локальная версия Knex: 0.20.3».

Полный файл миграции выглядит следующим образом:

const Coupon = require('../../models/coupon');
const Plan = require('../../models/plan');

exports.up = knex =>
  knex.schema
  .table('transactions', table => {
    table.decimal('plan_price', 10, 2);
    table.decimal('discount', 10, 2).defaultTo(0);
  })
  .then(() => {
    return knex('transactions').then(async transactions => {
      for (let trans of transactions) {
          let original_price;
          let total_coupon_discount = 0;

          const plan = await Plan.where({id: trans.plan_id}).fetch();
          if (plan) {
            original_price = plan.get("price");
          } else {
            original_price = null;
          }

          if (trans.coupon_id) {
            const coupon = await Coupon.where({id: trans.coupon_id}).fetch();
            if (coupon) {
              const amount_ex_vat = trans.amount_ex_vat;
              const couponAmount = coupon.get("discount_amount");
              let couponPercentage = 1;
              if ( coupon.get("discount_percentage") > 0 ) {
                couponPercentage = 1.0 / ( (100.0 - coupon.get("discount_percentage")) / 100.0 );
              }
              original_price = (amount_ex_vat * couponPercentage) + couponAmount;
              total_coupon_discount = original_price - amount_ex_vat;
            }
          }

          await knex("transactions")
            .where('id', '=', trans.id)
            .update({
              plan_price: original_price,
              discount: total_coupon_discount
            }).catch(function(error){
              console.log(error)
            }).then(function(){
              console.log('  • Added original price and discount to transaction: ' + trans.id);
            })
      }
    });
  });

exports.down = function (knex) {
  return knex.schema.table('transactions', (table) => {
    table.dropColumn('plan_price');
    table.dropColumn('discount');
  });
};

1 Ответ

2 голосов
/ 06 января 2020

Knex больше не принимает второй параметр Promise, поскольку некоторое время назад он перешел к использованию собственных обещаний. Таким образом, Promise означает undefined в вашей миграции, поэтому свойства .resolve определенно не существует.

Совершенно странно, что кто-то считает возвращение Promise.resolve().then хорошей идеей в любом случае. Вам нужно выполнить модификацию схемы, а затем модификацию данных. Это будет выглядеть примерно так:

return knex.schema.table("transactions", t => {
  t.decimal('plan_price', 10, 2);
  // etc
})
  .then(() =>
    knex("transactions")
      .then(
        // Update values here
      );
  )
  .catch(console.error)

Кроме того, вы обнаруживаете, что for_each не всегда очень хорошо подходит для асинхронной работы. Однако мы все же можем заставить эту миграцию работать, либо написав более сложный запрос (объединяя другие таблицы, чтобы получить значения, которые мы ищем), либо изменив текущую, чтобы она лучше работала с обещаниями:

exports.up = knex =>
  knex.schema
    .table("transactions", t => {
      t.decimal("plan_price", 10, 2);
      t.decimal("discount", 10, 2).defaultTo(0);
    })
    .then(() => {
      return knex("transactions").then(async transactions => {
        for (let trans of transactions) {
          let original_price;
          let total_coupon_discount = 0;

          const plan = await Plan.where({ id: trans.plan_id }).fetch();
          if (plan) {
            original_price = plan.get("price");
          }

          if (trans.coupon_id) {
            const coupon = await Coupon.where({ id: trans.coupon_id }).fetch();
            if (coupon) {
              total_coupon_discount = coupon.get("discount_amount");
              original_price = trans.amount_ex_vat + couponAmount;
            }
          }

          await knex("transactions")
            .where("id", "=", trans.id)
            .update({
              plan_price: original_price,
              discount: total_coupon_discount
            });
        }
      });
    });

Я, очевидно, не могу говорить о правильности вашего другого кода (выглядит для меня как Книжная полка?), Потому что у меня нет вашей схемы, но это общая идея. В основном, когда вы хотите использовать async / await you go с for... of, сохраняя все в порядке и последовательно.

Я думаю, что стоит отметить, что этот подход может быть довольно медленным на большом таблица с тремя отдельными запросами блокировки на строку transactions.

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