Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fs_attachment/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"name": "Base Attachment Object Store",
"summary": "Store attachments on external object store",
"version": "16.0.2.0.1",
"version": "16.0.2.0.2",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The version should not be bump manually. The merge bot will do it ;-)

"author": "Camptocamp, ACSONE SA/NV, Odoo Community Association (OCA)",
"license": "AGPL-3",
"development_status": "Beta",
Expand Down
12 changes: 12 additions & 0 deletions fs_attachment/models/fs_file_gc.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ def _in_new_cursor(self) -> Cursor:

with closing(self.env.registry.cursor()) as cr:
try:
# Bound this cursor so it cannot hold a row lock on
# fs_file_gc indefinitely. Without these guards, a slow
# external storage backend (e.g. S3, Azure Blob) can leave
# the cursor "idle in transaction" while waiting on I/O,
# serialising every attachment write behind the unique
# constraint lock on fs_file_gc.store_fname. Every queued
# POST /mail/attachment/upload then times out at the
# session statement_timeout and returns a 500 HTML page,
# which the frontend tries to JSON.parse and fails with
# "Unexpected token '<', \"<!DOCTYPE\"...".
cr.execute("SET LOCAL statement_timeout = '30s'")
cr.execute("SET LOCAL idle_in_transaction_session_timeout = '60s'")
yield cr
except Exception:
cr.rollback()
Expand Down
15 changes: 15 additions & 0 deletions fs_attachment/readme/newsfragments/gc_cursor_timeout.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Bound the GC cursor with per-transaction timeouts.

The secondary cursor opened by ``FsFileGC._in_new_cursor`` had no upper
bound, so a slow or unresponsive external storage backend (observed on
Azure Blob, same class of issue applies to S3) could leave it
``idle in transaction`` while waiting on network I/O. The cursor kept a
row lock on ``fs_file_gc`` (via the ``store_fname`` unique constraint in
``_mark_for_gc``), serialising every concurrent attachment write until
the Odoo session ``statement_timeout`` killed it — by which time every
``POST /mail/attachment/upload`` was returning a 500 HTML page, which
the frontend tried to ``JSON.parse`` and failed with
``Unexpected token '<', "<!DOCTYPE"...``. A ``SET LOCAL statement_timeout``
and ``SET LOCAL idle_in_transaction_session_timeout`` on the new cursor
cap the damage to 30-60 s and let the main transaction fail fast instead
of stalling the whole instance.
Loading