Skip to content

Conversation

@cikasfm
Copy link
Contributor

@cikasfm cikasfm commented Aug 18, 2025

Introducing new on_duplicate and on_missing flags for Write API to allow future implementation of writing duplicate tuples and deleting tuples that don't exist.

Description

This pull request updates Write API contract as required for the related issue openfga/roadmap#79. This feature allows API requests to be sent without failing due to duplicate or missing tuples, which significantly improves the developer experience.

What problem is being solved?

The current Write API fails an entire batched request if any single tuple being written already exists. This forces developers to implement complex client-side logic for pre-checking for existing tuples or handling de-duplication in their retry mechanisms. This behavior makes batched operations brittle and is a common source of developer friction.

How is it being solved?

The solution introduces optional on_duplicate and on_missing flags to the Write API request. These flags allow a developer to specify that duplicate writes or missing deletes should be ignored as a no-op, rather than causing the entire request to fail. The backend logic will be a SQL implementation that uses a single, atomic database transaction to ensure consistency and prevent race conditions.

What changes are made to solve it?

Example:

{
  "writes": {
    "tuple_keys": [
      {
        "user": "user:anne",
        "relation": "reader",
        "object": "document:2021-budget",
        "condition": {
          "name": "condition1",
          "context": {}
        }
      }
    ],
    "on_duplicate": "ignore"
  },
  "deletes": {
    "tuple_keys": [
      {
        "user": "user:anne",
        "relation": "reader",
        "object": "document:2021-budget"
      }
    ],
    "on_missing": "ignore"
  },
  "authorization_model_id": "01G5JAVJ41T49E9TT3SKVS7X1J"
}

Swagger API

Screenshot 2025-08-26 at 11 32 29 AM Screenshot 2025-08-26 at 11 31 24 AM

References

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

@cikasfm cikasfm requested review from a team as code owners August 18, 2025 20:58
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 18, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbit review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Introduces configurable idempotence controls for the Write API by adding OnDuplicate and OnMissing enums, extending write/delete request schemas and proto messages with corresponding fields, updating OpenAPI docs, and adding enum validations. Also adjusts README code fence languages for syntax highlighting.

Changes

Cohort / File(s) Summary
Docs formatting
README.md
Changed code fences from toshell in two sections; no content changes.
OpenAPI spec: Write API idempotence
docs/openapiv2/apidocs.swagger.json
Added OnDuplicate and OnMissing enum definitions; extended WriteRequestWrites with on_duplicate and WriteRequestDeletes with on_missing; updated /write operation descriptions and examples.
Proto: Write API enums and fields
openfga/v1/openfga_service.proto
Added enums OnDuplicate and OnMissing; added on_duplicate to WriteRequestWrites and on_missing to WriteRequestDeletes; updated comments/docs.
Validation: enum checks
proto/openfga/v1/openfga_service.pb.validate.go
Added validations ensuring on_duplicate/on_missing are valid enum values; integrated with existing multi-error handling.

Sequence Diagram(s)

sequenceDiagram
  actor Client
  participant API as OpenFGA Write API
  participant Store as Tuple Store

  Client->>API: WriteRequest(writes{on_duplicate}, deletes{on_missing})
  API->>API: Validate enums (on_duplicate/on_missing)
  API->>Store: Apply writes
  alt on_duplicate=ERROR and tuple exists
    Store-->>API: Duplicate detected
    API-->>Client: Error (duplicate)
  else on_duplicate=IGNORE or not duplicate
    Store-->>API: Write ok
    API->>Store: Apply deletes
    alt on_missing=ERROR and tuple missing
      Store-->>API: Missing detected
      API-->>Client: Error (missing)
    else on_missing=IGNORE or exists
      Store-->>API: Delete ok
      API-->>Client: Success
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch duplicate-writes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@rhamzeh rhamzeh marked this pull request as draft August 18, 2025 21:00
@rhamzeh rhamzeh changed the title OpenFGA API Protobuf for Idempotent Writes feat: add support for Idempotent Writes Aug 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
README.md (1)

24-27: Align code fence language specifier with the rest of the doc (use bash).

Earlier blocks in this file use bash. For consistency and better syntax highlighting, prefer bash here too.

Apply this diff:

-    ```shell
+    ```bash
@@
-    ```shell
+    ```bash

Also applies to: 29-31

openfga/v1/openfga_service.proto (3)

157-160: Tighten wording and remove a stray double-space in Write API description.

Minor copy edits and clarify where to set the flags.

-        "The API is not idempotent by default: if, later on, you try to add the same tuple key (even if the `condition` is different), or if you try to delete a non-existing tuple, it will throw an error.\n"
-        "In order to allow writes in case an identical tuple already exists in the database, you may modify the behavior using `\"on_duplicate\": \"ON_DUPLICATE_IGNORE\"`\n"
-        "In order to allow deletes in case a tuple was already deleted from  the database, you may modify the behavior using `\"on_missing\": \"ON_MISSING_IGNORE\"`\n"
+        "The API is not idempotent by default: if, later on, you try to add the same tuple key (even if the `condition` is different), or if you try to delete a non-existing tuple, it will throw an error.\n"
+        "To allow writes when an identical tuple already exists in the database, set `\"on_duplicate\": \"ON_DUPLICATE_IGNORE\"` on the `writes` object.\n"
+        "To allow deletes when a tuple was already removed from the database, set `\"on_missing\": \"ON_MISSING_IGNORE\"` on the `deletes` object.\n"

1248-1252: Correct OnDuplicate comments: remove “context” and fix minor grammar/spacing.

“Context” is not part of tuple attributes for writes; also tighten phrasing.

 enum OnDuplicate {
-  ON_DUPLICATE_UNSPECIFIED = 0; // If set to 'ON_DUPLICATE_UNSPECIFIED' OR not set, the API will behave as if it was set to 'ON_DUPLICATE_ERROR'.
-  ON_DUPLICATE_ERROR = 1; // If set to 'ON_DUPLICATE_ERROR', the API will return an error if a tuple already exists.
-  ON_DUPLICATE_IGNORE = 2; // If set to 'ON_DUPLICATE_IGNORE', the API will not return an error if a tuple already exists and all the attributes (including condition and context ) are matching.
+  ON_DUPLICATE_UNSPECIFIED = 0; // If set to 'ON_DUPLICATE_UNSPECIFIED' or not set, the API behaves as if it were set to 'ON_DUPLICATE_ERROR'.
+  ON_DUPLICATE_ERROR = 1; // Return an error if a tuple already exists.
+  ON_DUPLICATE_IGNORE = 2; // Treat identical writes as no-ops if all attributes (including the condition) match.
 }

1261-1269: Clarify field descriptions for on_duplicate/on_missing.

Make field help text explicit about what “identical” means and avoid ambiguity around “attributes”.

   OnDuplicate on_duplicate = 2 [
     json_name = "on_duplicate",
     (validate.rules).enum.defined_only = true,
     (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
       example: "\"ON_DUPLICATE_ERROR\""
-      description: "If set to 'ON_DUPLICATE_ERROR', the API will return an error if a tuple already exists. If set to 'ON_DUPLICATE_IGNORE', the API will not return an error if a tuple already exists and all the attributes are matching."
+      description: "If 'ON_DUPLICATE_ERROR', the API returns an error if an identical tuple already exists. If 'ON_DUPLICATE_IGNORE', identical writes are treated as no-ops (matching on user, relation, object, and condition)."
     }
   ];
   OnMissing on_missing = 2 [
     json_name = "on_missing",
     (validate.rules).enum.defined_only = true,
     (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
       example: "\"ON_MISSING_ERROR\""
-      description: "If set to 'ON_MISSING_ERROR', the API will return an error if a tuple does not exist. If set to 'ON_MISSING_IGNORE', the API will not return an error if a tuple does not exist."
+      description: "If 'ON_MISSING_ERROR', the API returns an error when deleting a tuple that does not exist. If 'ON_MISSING_IGNORE', deletes of non-existent tuples are treated as no-ops."
     }
   ];

Also applies to: 1284-1291

docs/openapiv2/apidocs.swagger.json (2)

2414-2423: Tighten OnDuplicate description formatting and clarify equality criteria

There’s a stray space before the closing parenthesis and the equality criteria can be clarified.

- "description": " - ON_DUPLICATE_UNSPECIFIED: If set to 'ON_DUPLICATE_UNSPECIFIED' OR not set, the API will behave as if it was set to 'ON_DUPLICATE_ERROR'.\n - ON_DUPLICATE_ERROR: If set to 'ON_DUPLICATE_ERROR', the API will return an error if a tuple already exists.\n - ON_DUPLICATE_IGNORE: If set to 'ON_DUPLICATE_IGNORE', the API will not return an error if a tuple already exists and all the attributes (including condition and context ) are matching."
+ "description": " - ON_DUPLICATE_UNSPECIFIED: If set to 'ON_DUPLICATE_UNSPECIFIED' OR not set, the API will behave as if it was set to 'ON_DUPLICATE_ERROR'.\n - ON_DUPLICATE_ERROR: If set to 'ON_DUPLICATE_ERROR', the API will return an error if a tuple already exists.\n - ON_DUPLICATE_IGNORE: If set to 'ON_DUPLICATE_IGNORE', the API will not return an error if a tuple already exists and all attributes match (including the condition name and its persisted context)."

3108-3113: Align on_duplicate property description with enum semantics

To avoid ambiguity, mirror the enum’s wording and explicitly mention condition/context equality.

- "description": "If set to 'ON_DUPLICATE_ERROR', the API will return an error if a tuple already exists. If set to 'ON_DUPLICATE_IGNORE', the API will not return an error if a tuple already exists and all the attributes are matching."
+ "description": "If set to 'ON_DUPLICATE_ERROR', the API returns an error if a tuple already exists. If set to 'ON_DUPLICATE_IGNORE', the API will not return an error if a tuple already exists and all attributes match (including the condition name and its persisted context)."
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c0b62b2 and cea8e59.

⛔ Files ignored due to path filters (1)
  • proto/openfga/v1/openfga_service.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (4)
  • README.md (1 hunks)
  • docs/openapiv2/apidocs.swagger.json (4 hunks)
  • openfga/v1/openfga_service.proto (5 hunks)
  • proto/openfga/v1/openfga_service.pb.validate.go (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
proto/openfga/v1/openfga_service.pb.validate.go (1)
proto/openfga/v1/openfga_service.pb.go (2)
  • OnDuplicate_name (40-44)
  • OnMissing_name (89-93)
🔇 Additional comments (2)
proto/openfga/v1/openfga_service.pb.validate.go (1)

1749-1758: LGTM: enum validation added for on_duplicate/on_missing.

The checks correctly ensure values are among defined enum members, aligning with the proto validate annotations. No action needed.

Also applies to: 1907-1916

docs/openapiv2/apidocs.swagger.json (1)

3087-3092: LGTM: on_missing field added correctly to WriteRequestDeletes

The new optional on_missing enum reference and description are correctly modeled and consistent with the new OnMissing definition.

cikasfm and others added 2 commits August 18, 2025 14:10
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
adriantam
adriantam previously approved these changes Aug 18, 2025
@cikasfm cikasfm marked this pull request as ready for review August 18, 2025 22:41
adriantam
adriantam previously approved these changes Aug 18, 2025
@cikasfm cikasfm changed the title feat: add support for Idempotent Writes feat: add support for Writes with on_duplicate and on_missing options Aug 18, 2025
@cikasfm cikasfm changed the title feat: add support for Writes with on_duplicate and on_missing options feat: add support for Write API with on_duplicate and on_missing options Aug 18, 2025
Copy link
Member

@rhamzeh rhamzeh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we:

  1. Keep this in draft until the RFC is up and approved and the code in openfga/openfga is ready for it? Otherwise we'll have it merged and SDKs and docs will start including it while it's not ready.
  2. While I'm OK with this in general - can we ideate on other ideas? Such as combining write and delete enums? e.g. on_conflict

On conflict:

  • Error
  • Update
  • Ignore
  1. I would prefer that in the enum, on error (being the current functionality and the default) is either the first (replacing unspecified) or the second (after unspecified), currently, the flow of:
  • unspecified (error)
  • ignore (no error)
  • error (error)
    seems weird

Also ignore should only ignore if the tuples are EXACT matches. If the conditions are different, it absolutely should error as you are in an unexpected state as far as the user is concerned.

You may want to support on conflict update, but that can also come later if implementation delays this work

@adriantam adriantam marked this pull request as draft August 19, 2025 01:12
@adriantam
Copy link
Member

Can we:

  1. Keep this in draft until the RFC is up and approved and the code in openfga/openfga is ready for it? Otherwise we'll have it merged and SDKs and docs will start including it while it's not ready.
  2. While I'm OK with this in general - can we ideate on other ideas? Such as combining write and delete enums? e.g. on_conflict

On conflict:

  • Error
  • Update
  • Ignore
  1. I would prefer that in the enum, on error (being the current functionality and the default) is either the first (replacing unspecified) or the second (after unspecified), currently, the flow of:
  • unspecified (error)
  • ignore (no error)
  • error (error)
    seems weird

Also ignore should only ignore if the tuples are EXACT matches. If the conditions are different, it absolutely should error as you are in an unexpected state as far as the user is concerned.

You may want to support on conflict update, but that can also come later if implementation delays this work

  1. Moved to in-draft status
  2. This is already in unspecified (error), error, ignore.

@cikasfm cikasfm marked this pull request as ready for review September 9, 2025 17:22
@cikasfm cikasfm merged commit b4b2a12 into main Sep 9, 2025
11 checks passed
@cikasfm cikasfm deleted the duplicate-writes branch September 9, 2025 17:22
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.

4 participants