Release notes v0.0.115

This release is not that big, but still worth for proper release notes. We go over the changes package by package.

Code gen

It does not feel like a proper Compas release without some fixes or features added to the code generators, as is the case this time. The table traverser is a relative new feature added in Compas v0.0.103. At that point we did not have a query builder, and generated a stand-alone table traverser that unlocked the following chains:

// Get all users that liked the specific post
const users = queryUser().viaLikes().viaPosts({ id: postId });

This was straight forward to use, but limited in options. You could not use this to filter on two separate ' traverse'-chains.

Next came the query-builder with its built-in support for table traversal. This unlocked traversing via multiple chains, however it was not documented as such. To explain why, let us do a run down of what the traverser does.

Provided the following structure:

User:
- id: uuid
- name: string

Post:
- id: uuid
- writer: uuid -> User
- title: string

Like:
- id: uuid
- user: uuid -> User
- post: uuid -> Post

We have User, Post and Like as our tables. The relation being that a User is the writer of many Post, and a User can Like many Post. To get all users that have written at least a single post, we can do the following:

const users = await queryUser({
  where: {},
  viaPosts: {},
}).exec(sql);

Internally, the viaPosts was converted to a QueryPart and thus allowed to be set on the where-object as the idIn field. The QueryPart was a query selecting the writer field from Post.

If we do one of the following things you would get unexpected results:

// Get users that have written a post and that are in our selection in 'idIn'.
const users = await queryUser({
  where: {
    idIn: ["uuid-110", "uuid-120", "uuid-130"],
  },
  viaPosts: {},
}).exec(sql);

// Get users that have written a post and have liked an post
const users = await queryUser({
  where: {},
  viaPosts: {},
  viaLikes: {},
}).exec(sql);

We have a problem in both cases. In our first case we already set idIn however viaPosts will overwrite it. In the second case, since both Like and Post are referenced via the id on a User, viaLikes would overwrite viaPosts since they both set directly the idIn property on the provided where object.

Of course not all references would go via the idIn parameter, so indirectly the generated traverser could work with multiple chains, but would fail on the most useful cases.

In this release we solve these cases. We now generate an INTERSECT query from all viaXxx calls and if necessary combine it with an existing idIn array. INTERSECT gives us the same AND guarantees as normal where filters.

Store

The JobQueueWorker got two upgrades as well, breaking existing behaviour.

Managed retries:

The worker now won't leak any errors that are thrown by job handler invocations. Instead, all errors are caught. On error the job id, name, retryCount and error are logged via an error log. The retryCount is then increased and sql transaction is committed.

By adding a savepoint, we can rollback any queries done by the job handler. Note that you won't see failed transactions from the worker anymore, so that metric may become less relevant for you. We added a maxRetryCount to the options that can be provided to the constructor, it defaults to 5.

Handler timeouts

To prevent that the worker is too busy with a job, comes in a infinite async loop or for some other reason takes long to complete. We now automatically stop the job and let it be processed as if an error was thrown by the handler. Which results in an increased retryCount. Note that this won't work correctly if the job does only synchronous processing.

The timeout can be configured via the constructor options under handlerTimeout in milliseconds, and defaults to 30 seconds.

In closing

Some other chores are being done as well, like writing docs (A). The Compas repo will now require use of conventional-commit like specifiers in the commit title, like feat, fix, etc. This was decided after some feedback to get a quicker overview of the current state of development.

I am also experimenting a bit with JSDoc based documentation again. We had TSDoc based documentation for a while, based on the index.d.ts files that we provide. The output was too unstructured, and overall hard to use in any form. If anyone has any suggestions please let me know directly or via the Align JSDoc issue.