Uploaded image for project: 'RAML Module Builder'
  1. RAML Module Builder
  2. RMB-719

SPIKE: design protocol and implementation for optimistic locking

    XMLWordPrintable

    Details

    • Template:
    • Sprint:
      CP: sprint 97, CP: sprint 98, CP: sprint 99, CP: sprint 100, CP: sprint 101
    • Development Team:
      Core: Platform

      Description

      See UXPROD-1752 for an overview.

      So far we have discussed a couple of different options for the protocol:

      1. use ETag response header with If-Match conditional request. Respond with 412 Precondition Failed on collision detection

      • Pros: standard HTTP feature for OL, supported by browsers, easy to implement – no API changes in FOLIO besides new error code
      • Cons: works on resource-level so handling individual records during batch updates (collections) is not supported

      2. put the "etag" in the entity schema (body). Client repeats server-provided value. Respond with 409 Conflict on collision detection.

      • Pros: solves the problem with updating individual records during batch updates
      • Cons: non-standard, requires changes to entity schemas

      Implementation notes

      ETags can be either "strong" (byte-for-byte similarity) or "weak" (semantic similarity).

      Etags can be generated as hashes/checksums, as a revision number or as a date.

      Option 2 may work similar to how metadata.schema works. Though can't use the metada.schema itself because it's marked readonly when used.

      Option 2 may not be immediately useful as FOLIO does not support partial batch updates anyway.

      We need to decide if the OL functionality is optional or required: e.g in Option 1 always fail with 412 Precondition Failed when no If-Match supplied, in Option 2 require the etag field.

      We need to decide how we support batch updates. Quote from UXPROD-1752: "This situation can happen when multiple data imports are happening at the same time (or data import and a user acting on the same record at the same time) and can affect many records at the same time.")

      Update examples if "version" property is optional
      old is the value stored in the module/database, req is the record of the API request, new is the value stored in the module/database after the request

      Missing "version" property is treated as if it were "version": 0.

      old: { "id":"12..89", "name": "foo", "version": 3 }
      req: { "id":"12..89", "name": "bar", "version": 3 }  succeeds
      new: { "id":"12..89", "name": "bar", "version": 4 }
      
      old: { "id":"12..89", "name": "foo", "version": 3 }
      req: { "id":"12..89", "name": "bar", "version": 2 }  rejected
      new: { "id":"12..89", "name": "foo", "version": 3 }
      
      old: { "id":"12..89", "name": "foo" }
      req: { "id":"12..89", "name": "bar" }                succeeds
      new: { "id":"12..89", "name": "bar", "version": 1 }
      
      old: { "id":"12..89", "name": "foo" }
      req: { "id":"12..89", "name": "bar", "version": 1 }  rejected
      new: { "id":"12..89", "name": "foo" }
      
      old: { "id":"12..89", "name": "foo", "version": 1 }
      req: { "id":"12..89", "name": "bar" }                rejected
      new: { "id":"12..89", "name": "foo", "version": 1 }
      

      Update examples if "version" property is required

      old: { "id":"12..89", "name": "foo", "version": 3 }
      req: { "id":"12..89", "name": "bar", "version": 3 }  succeeds
      new: { "id":"12..89", "name": "bar", "version": 4 }
      
      old: { "id":"12..89", "name": "foo", "version": 3 }
      req: { "id":"12..89", "name": "bar", "version": 2 }  rejected
      new: { "id":"12..89", "name": "foo", "version": 3 }
      
      old: { "id":"12..89", "name": "foo"} --> migrates to { "id":"12..89", "name": "foo", "version": "1"}
      req: { "id":"12..89", "name": "bar" }    rejected -- version is required UNLESS we treat missing value as the initial value "1" 
      new: { "id":"12..89", "name": "bar", "version": 1 }
      
      old: { "id":"12..89", "name": "foo"} --> migrates to { "id":"12..89", "name": "foo", "version": "1"}
      req: { "id":"12..89", "name": "bar", "version": 1 }  succeeds -- assuming old value would have been migrated to "1" on upgrade
      new: { "id":"12..89", "name": "bar, "version": 2 }
      
      old: { "id":"12..89", "name": "foo", "version": 1 }
      req: { "id":"12..89", "name": "bar" }                rejected -- version is required
      new: { "id":"12..89", "name": "foo", "version": 1 }
      

        TestRail: Results

          Attachments

            Issue Links

              Activity

                People

                Assignee:
                jakub Jakub Skoczen
                Reporter:
                jakub Jakub Skoczen
                Votes:
                0 Vote for this issue
                Watchers:
                9 Start watching this issue

                  Dates

                  Created:
                  Updated:
                  Resolved:

                    TestRail: Runs

                      TestRail: Cases