michaellperry / jinaga

Universal web back-end, offering an application-agnostic API, real-time collaboration, and conflict resolution.
http://jinaga.com
MIT License
35 stars 3 forks source link

Creating a fact with an unsaved predecessor results in 500 #26

Closed michaellperry closed 6 years ago

michaellperry commented 6 years ago

Reproduction steps:

Use j.fact twice to create a new predecessor, and a new successor. For example:

var predecessor = predecessor: {
  value: 'Never seen before',
  root: {
    value: 'This one already existed'
  }
};
j.fact(predecessor);

j.fact({
  predecessor: predecessor,
  value: 'Brand new, too'
}

Outcome

Because the first j.fact is not awaited, this creates a race condition between saving the predecessor and the successor. Both save methods go to the server simultaneously, and are handled in parallel. When the server succeeds in saving the predecessor, the successor does not always see that the predecessor exists. Sometimes, it tries to save the predecessor again itself.

The database provider throws duplicate key value violates unique constraint "ux_edge" because the edge between the predecessor and the root was created when the predecessor was saved. The successor save does not accept this exception as a success.

This aborts the save of the fact, and so the successor fact is never saved.

Expected behavior

The exception from the database should be ignored. The record that we are trying to insert already exists.

michaellperry commented 6 years ago

Added ON CONFLICT DO NOTHING to the PostgreSQL INSERT statements so that duplicate keys are ignored.