nextcloud / nextcloud/server

[Bug]: Dirty user_status table read

Open
#43,109 1 comment 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage 29-feedback bug feature: database feature: status
Dominant language
PHP
Stars
36.9k
Forks
5.2k
Avg merge
2d 3h
Merged PRs (30d)
713

Description

⚠️ This issue respects the following points: ⚠️
Bug description

user_status table is written and read within the same process.

{
  "reqId": "fxUYHfQwOF9dq7xnFck2",
  "level": 0,
  "time": "2024-01-23T11:06:42+00:00",
  "remoteAddr": "127.0.0.1",
  "user": "admin",
  "app": "no app in context",
  "method": "PUT",
  "url": "/ocs/v2.php/apps/user_status/api/v1/heartbeat?format=json",
  "message": "dirty table reads: SELECT * FROM `*PREFIX*user_status` WHERE `user_id` = :dcValue1",
  "userAgent": "Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0",
  "version": "29.0.0.4",
  "exception": {
    "Exception": "Exception",
    "Message": "",
    "Code": 0,
    "Trace": [
      {
        "file": "/home/christoph/workspace/nextcloud/3rdparty/doctrine/dbal/src/Query/QueryBuilder.php",
        "line": 337,
        "function": "executeQuery",
        "class": "OC\\DB\\Connection",
        "type": "->",
        "args": [
          "SELECT * FROM `*PREFIX*user_status` WHERE `user_id` = :dcValue1",
          [
            "admin"
          ],
          [
            2
          ],
          null
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/3rdparty/doctrine/dbal/src/Query/QueryBuilder.php",
        "line": 377,
        "function": "executeQuery",
        "class": "Doctrine\\DBAL\\Query\\QueryBuilder",
        "type": "->",
        "args": []
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/DB/QueryBuilder/QueryBuilder.php",
        "line": 280,
        "function": "execute",
        "class": "Doctrine\\DBAL\\Query\\QueryBuilder",
        "type": "->",
        "args": []
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/DB/QueryBuilder/QueryBuilder.php",
        "line": 293,
        "function": "execute",
        "class": "OC\\DB\\QueryBuilder\\QueryBuilder",
        "type": "->",
        "args": []
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/public/AppFramework/Db/QBMapper.php",
        "line": 275,
        "function": "executeQuery",
        "class": "OC\\DB\\QueryBuilder\\QueryBuilder",
        "type": "->",
        "args": []
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/public/AppFramework/Db/QBMapper.php",
        "line": 361,
        "function": "findOneQuery",
        "class": "OCP\\AppFramework\\Db\\QBMapper",
        "type": "->",
        "args": [
          [
            "OC\\DB\\QueryBuilder\\QueryBuilder"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/apps/user_status/lib/Db/UserStatusMapper.php",
        "line": 112,
        "function": "findEntity",
        "class": "OCP\\AppFramework\\Db\\QBMapper",
        "type": "->",
        "args": [
          [
            "OC\\DB\\QueryBuilder\\QueryBuilder"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/apps/user_status/lib/Service/StatusService.php",
        "line": 138,
        "function": "findByUserId",
        "class": "OCA\\UserStatus\\Db\\UserStatusMapper",
        "type": "->",
        "args": [
          "admin"
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/apps/user_status/lib/Listener/UserLiveStatusListener.php",
        "line": 74,
        "function": "findByUserId",
        "class": "OCA\\UserStatus\\Service\\StatusService",
        "type": "->",
        "args": [
          "admin"
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/EventDispatcher/ServiceEventListener.php",
        "line": 86,
        "function": "handle",
        "class": "OCA\\UserStatus\\Listener\\UserLiveStatusListener",
        "type": "->",
        "args": [
          [
            "OCP\\User\\Events\\UserLiveStatusEvent"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/3rdparty/symfony/event-dispatcher/EventDispatcher.php",
        "line": 230,
        "function": "__invoke",
        "class": "OC\\EventDispatcher\\ServiceEventListener",
        "type": "->",
        "args": [
          [
            "OCP\\User\\Events\\UserLiveStatusEvent"
          ],
          "OCP\\User\\Events\\UserLiveStatusEvent",
          [
            "Symfony\\Component\\EventDispatcher\\EventDispatcher"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/3rdparty/symfony/event-dispatcher/EventDispatcher.php",
        "line": 59,
        "function": "callListeners",
        "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
        "type": "->",
        "args": [
          [
            [
              "Closure"
            ]
          ],
          "OCP\\User\\Events\\UserLiveStatusEvent",
          [
            "OCP\\User\\Events\\UserLiveStatusEvent"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/EventDispatcher/EventDispatcher.php",
        "line": 94,
        "function": "dispatch",
        "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
        "type": "->",
        "args": [
          [
            "OCP\\User\\Events\\UserLiveStatusEvent"
          ],
          "OCP\\User\\Events\\UserLiveStatusEvent"
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/EventDispatcher/EventDispatcher.php",
        "line": 106,
        "function": "dispatch",
        "class": "OC\\EventDispatcher\\EventDispatcher",
        "type": "->",
        "args": [
          "OCP\\User\\Events\\UserLiveStatusEvent",
          [
            "OCP\\User\\Events\\UserLiveStatusEvent"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/apps/user_status/lib/Controller/HeartbeatController.php",
        "line": 100,
        "function": "dispatchTyped",
        "class": "OC\\EventDispatcher\\EventDispatcher",
        "type": "->",
        "args": [
          [
            "OCP\\User\\Events\\UserLiveStatusEvent"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/AppFramework/Http/Dispatcher.php",
        "line": 232,
        "function": "heartbeat",
        "class": "OCA\\UserStatus\\Controller\\HeartbeatController",
        "type": "->",
        "args": [
          "online"
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/AppFramework/Http/Dispatcher.php",
        "line": 138,
        "function": "executeController",
        "class": "OC\\AppFramework\\Http\\Dispatcher",
        "type": "->",
        "args": [
          [
            "OCA\\UserStatus\\Controller\\HeartbeatController"
          ],
          "heartbeat"
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/AppFramework/App.php",
        "line": 184,
        "function": "dispatch",
        "class": "OC\\AppFramework\\Http\\Dispatcher",
        "type": "->",
        "args": [
          [
            "OCA\\UserStatus\\Controller\\HeartbeatController"
          ],
          "heartbeat"
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/lib/private/Route/Router.php",
        "line": 315,
        "function": "main",
        "class": "OC\\AppFramework\\App",
        "type": "::",
        "args": [
          "OCA\\UserStatus\\Controller\\HeartbeatController",
          "heartbeat",
          [
            "OC\\AppFramework\\DependencyInjection\\DIContainer"
          ],
          [
            "ocs.user_status.Heartbeat.heartbeat"
          ]
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/ocs/v1.php",
        "line": 65,
        "function": "match",
        "class": "OC\\Route\\Router",
        "type": "->",
        "args": [
          "/ocsapp/apps/user_status/api/v1/heartbeat"
        ]
      },
      {
        "file": "/home/christoph/workspace/nextcloud/ocs/v2.php",
        "line": 23,
        "args": [
          "/home/christoph/workspace/nextcloud/ocs/v1.php"
        ],
        "function": "require_once"
      }
    ],
    "File": "/home/christoph/workspace/nextcloud/lib/private/DB/Connection.php",
    "Line": 284,
    "message": "dirty table reads: SELECT * FROM `*PREFIX*user_status` WHERE `user_id` = :dcValue1",
    "tables": [
      "oc_user_status"
    ],
    "reads": [
      "oc_user_status"
    ],
    "exception": {},
    "CustomMessage": "dirty table reads: SELECT * FROM `*PREFIX*user_status` WHERE `user_id` = :dcValue1"
  }
}

Options:

  1. Prevent reading at all by keeping the written data in memory and using that.
  2. Wrap write+read in a transaction if the two operations depend on each other.
Steps to reproduce
  1. Set log level to debug
  2. Load a Nextcloud page
Expected behavior

No dirty read logs

Installation method

None

Nextcloud Server version

master

Operating system

None

PHP engine version

None

Web server

None

Database engine version

None

Is this bug present after an update or on a fresh install?

None

Are you using the Nextcloud Server Encryption module?

None

What user-backends are you using?
  • Default user-backend (database)
  • LDAP/ Active Directory
  • SSO - SAML
  • Other
Configuration report

No response

List of activated Apps

No response

Nextcloud Signing status

No response

Nextcloud Logs

No response

Additional info

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the debug log by loading a Nextcloud page, then trace the heartbeat path from apps/user_status/lib/Controller/HeartbeatController.php through UserLiveStatusListener.php, StatusService.php, and UserStatusMapper.php. Also inspect lib/private/DB/Connection.php where the dirty-read message is emitted. Done means the user_status write and read no longer produce dirty-read logs, with the intended handling approach decided.

Written by the indexing model from the issue text.

Assessment

Tech stack
php, sql
Domain
backend, database
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.