ObjectionJS model used in knex migration reports "relation does not exist" when running batch of migrations

Solution 1:

I have managed to solve the problem by realising two things:

  1. The migrations are ran in transactions, which suggests that the actual knex object used to communicate with the database is shared across migrations and is the same. It therefore matters which knex object is used.
  2. My setup with asynchronous configuration fetching resulted in multiple connections when running migrations that make use of the models, because models would initialise their own connections.

From there, the solution was pretty obvious: use the same knex object across all the models and migration commands. This could be achieved in a relatively easy manner, by tweaking the migration files that use models in a following way:

002_insert_data.js

// Import the model as previously (name can be changed for clarity).
const MyModelUnbound = require('./MyModel');


exports.up = async (knex) => {
  // Bind the existing knex connection to the model.
  const MyModel = MyModelUnbound.bindKnex(knex);

  await MyModel.query().insert({text: 'My Text'});
}

// ...

It's important to note, that the above code sets the knexfile configuration in the model, adding the knexSnakeCaseMapper plugin, which will not be applied to the knex configuration generated by the getKnexfile() function. This could be fixed by moving that configuration to the getKnexfile() method (or in case where the API is used, duplicating that definition in the knexfile configuration in that place).

This has fixed my issue completely and now running the migrations in batches works fine. One thing I am still not entirely sure about is why the initial behaviour actually takes place. The way I imagined the transactions working was on a migration basis (i.e. 1 migration = 1 transaction), which would suggest that things should work one way or another.

My current theory is that there might be some race condition for when the first migration's transaction is completed, and when the next connection is established for the models in second migration. Either way, binding the original knex object (built during the call to migrations API or CLI) solves the problem.