Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow overriding init fail handling #1342

Merged
merged 1 commit into from
Sep 8, 2020
Merged

feat: allow overriding init fail handling #1342

merged 1 commit into from
Sep 8, 2020

Conversation

benjie
Copy link
Member

@benjie benjie commented Sep 2, 2020

Previously, if a fatal error occurred that prevented building the GraphQL schema:

  • if retryOnInitFail was not set/falsy, the process would exit with code 34
  • if retryOnInitFail was truthy, PostGraphile would keep retrying forever, with exponential backoff

Fatal errors can include things like not being able to connect to the database, and not being able to build the schema due to a naming conflict. These issues could be solved (e.g. when the database comes online, or if someone adds a smart comment to the database) so for some users this made sense. For others, particularly those running multiple versions of PostGraphile through the same node process, it made less sense.

This PR gives users a third option; they may specify retryOnInitFail as a callback function. The function is called with the error that occurred, and the number of attempts that have so far been made. The function is expected to return a promise which:

  • should return true to retry
  • should return false or reject to abort

When returning true, the function is expected to perform exponential backoff to prevent overwhelming the database. An example function to perform exponential back-off and retry forever could be:

async retryOnInitFail(error, attempts) {
  const delay = Math.min(100 * Math.pow(attempts, 2), 30000);
  await sleep(delay);
  return true;
}

If the function returns false or rejects, PostGraphile will release any Postgres pool it has created (if it was passed an already created pool, it will not release that since that is not its responsibility) and any requests that come through in the interrim will be met with 503 status code and the resulting JSON {"errors":[{"message":"Failed to initialize GraphQL schema."}]}.

The callback could be used for other purposes too, such as to unmount the middleware or shut down the server. The following example server, for example, cleanly shuts down after 10 retries if an error occurs during startup.

const http = require('http');
const { postgraphile } = require('./build/postgraphile');

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const server = http.createServer(
  postgraphile('postgres://foo:bar@baz/qux', ['a', 'b', 'c', 'd'], {
    graphiql: true,
    enhanceGraphiql: true,
    async retryOnInitFail(error, attempts) {
      if (attempts >= 10) {
        server.close();
        return false;
      }
      const delay = Math.min(100 * Math.pow(attempts, 2), 30000);
      console.log(`ERROR OCCURRED: ${error} (attempts = ${attempts}, delay = ${delay})`);
      await sleep(delay);
      return true;
    },
  }),
);

server.listen(5001, () => {
  console.log('Server listening: http://localhost:5001/graphiql');
});

Fixes #1315
Fixes #1302
Fixes #1073

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide a callback function to handle serious errors
1 participant