AppFlowy-IO / AppFlowy-IO/AppFlowy-SelfHost-Commercial

Re-publishing a page tree fails with `duplicate key value violates unique constraint "af_published_blob_ref_pkey"

Aperta
#5 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
Go Template
Stelle
6
Fork
5
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

## Summary

On a self-hosted deployment, publishing a page tree succeeds the **first** time, but **every subsequent publish of any tree that contains an already-published file attachment fails**. The whole publish transaction is rolled back, so the page can never be re-published or published under a new parent.

The insert into `af_published_blob_ref` appears to be missing an `ON CONFLICT DO NOTHING` (or an upsert), unlike the insert into `af_published_collab` right before it in the same transaction, which is already idempotent ("Inserting or replacing ...").

## Environment

- AppFlowy Cloud (self-hosted commercial fork): **0.17.8** — image `appflowyinc/appflowy_cloud:latest`, reported by the server as `Using AppFlowy Cloud version:0.17.8, using self-hosted version`
- Deployed with the official `docker-compose.yml`
- Storage: MinIO (`APPFLOWY_S3_USE_MINIO=true`)
- Postgres: `pgvector/pgvector:pg16`
- Client: AppFlowy desktop (macOS), `client_version` reported as `0.13.0`

## Steps to reproduce

1. Create a page tree that contains a page with a **file attachment** (in my case a `.pdf`).
2. Publish the top-level page. → **Succeeds.** A row is written to `af_published_blob_ref`.
3. Unpublish it from the app (optional — makes no difference).
4. Publish that page again, or publish any other page whose subtree includes the page holding that attachment. → **Fails every time.**

## Expected behaviour

The publish succeeds and the existing blob reference is reused / updated.

## Actual behaviour

The server returns **HTTP 200** with this error envelope (133 bytes):

```json
{"code":1020,"message":"error returned from database: duplicate key value violates unique constraint \"af_published_blob_ref_pkey\""}
```

The whole transaction is rolled back — including the ~19 rows that had just been written to `af_published_collab` — so nothing is published.

Because the error is returned with HTTP 200 and an error code the desktop client does not recognise, the UI only shows a generic **"internal error"**, which makes this very hard to diagnose from the app alone.

## Evidence

Relevant constraint:

```
af_published_blob_ref_pkey UNIQUE (workspace_id, view_id, s3_key)
```

Server log for a failing request (`RUST_LOG=debug`), showing the flow stopping exactly at the blob-ref insert:

```
INFO publish | Rewrote document assets for view_id=, found 0 blob keys
INFO publish | Extracted 1 file references from document
DEBUG publish | Inserting or replacing [ ...19 view ids... ]
INFO publish | Deleted existing published collab record with publish names: [...]
DEBUG publish | Inserting published blob refs for workspace_id: , view_id: ,
keys: ["public///=.pdf"]
DEBUG sqlx | INSERT INTO af_published_collab ( … rows_affected = 19
DEBUG sqlx | INSERT INTO af_published_blob_ref ( … rows_affected = 0

```

Note that **no `ERROR`/`WARN` level line is logged at all** for this failure, even at `RUST_LOG=debug`; the error is only visible in the HTTP response body. I had to capture the HTTP response on the wire to find out what was actually failing.

The pre-existing row that causes the collision (written by the first, successful publish):

```
workspace_id |
view_id |
s3_key | public///=.pdf
created_at | first successful publish
```

Side effect worth noting: the object **is** copied to `public/...` in S3/MinIO on every failed attempt (S3 is not part of the transaction), so failed publishes leave the copied object behind while the DB is rolled back.

## Impact

- Any page tree containing a file attachment can only ever be published **once**.
- Unpublishing from the app does **not** clean up `af_published_blob_ref`, so there is no in-app way to recover.
- Workarounds: delete the offending row from `af_published_blob_ref` manually, or remove the attachment from the document. I confirmed that removing the attachment makes the publish succeed immediately (the same request then returns a 56-byte success envelope instead of the 133-byte error).

## Suggested fix

Make the `af_published_blob_ref` insert idempotent, matching the behaviour of the `af_published_collab` insert in the same transaction, e.g.:

```sql
INSERT INTO af_published_blob_ref (workspace_id, view_id, s3_key)
VALUES (...)
ON CONFLICT (workspace_id, view_id, s3_key) DO NOTHING
```

It would also help a lot if this failure were logged at `ERROR` level, and if the desktop client surfaced the server-provided message instead of a generic "internal error".

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.