firebase / firebase/firebase-tools

Transaction lock timeout error

Open
#8,105 6 comments 0 reactions 0 assignees View on GitHub
emulators: firestore reproducible type: bug
Dominant language
TypeScript
Stars
4.5k
Forks
1.3k
Avg merge
1d 12h
Merged PRs (30d)
84

Description

### [REQUIRED] Environment info

**firebase-tools: 13.29.1**

**Platform: macOS**

### [REQUIRED] Test case

```
#!/usr/bin/env python3

import requests
import json
import sys

def main():
# Update these values based on your environment and project setup:
EMULATOR_HOST = "127.0.0.1:54510" # Where your Firestore/Datastore emulator is running
PROJECT_ID = "testbed-test" # Your GCP project ID

# 1. Construct the Datastore REST API endpoint for "lookup"
lookup_url = f"http://{EMULATOR_HOST}/v1/projects/{PROJECT_ID}:lookup"

# 2. Build the request body for the Datastore v1 "lookup"
# We use readOptions.newTransaction to start a new read-write transaction
lookup_payload = {
"readOptions": {
"newTransaction": {
"readWrite": {}
}
},
"keys": [
{
"path": [
{
"kind": "TestKind",
"name": "TestKey"
}
]
}
]
}

print("[LOOKUP REQUEST BODY]")
print(json.dumps(lookup_payload, indent=2))

# 3. Send the lookup request
lookup_resp = requests.post(lookup_url, json=lookup_payload)
print("\n[LOOKUP RESPONSE]")
print("Status Code:", lookup_resp.status_code)
print("Body:", lookup_resp.text)

# 4. Parse out the transaction ID from the lookup response
# According to the Datastore docs, if you start a transaction,
# the response will include a 'transaction' field.
if lookup_resp.status_code != 200:
print("Lookup request failed; cannot proceed.")
sys.exit(1)

lookup_data = lookup_resp.json()

transaction_id = lookup_data.get("transaction")
if not transaction_id:
print("No transaction ID returned in lookup response. Exiting.")
sys.exit(1)

print(f"\nExtracted Transaction ID: {transaction_id}")

# 5. Now use this transaction ID in a commit request
commit_url = f"http://{EMULATOR_HOST}/v1/projects/{PROJECT_ID}:commit"

# Example mutation: upsert an entity with the same key used in the lookup
commit_payload = {
"mode": "TRANSACTIONAL",
"transaction": transaction_id,
"mutations": [
{
"upsert": {
"key": {
"partitionId": {
"projectId": PROJECT_ID,
"namespaceId": ""
},
"path": [
{
"kind": "TestKind",
"name": "TestKey"
}
]
},
"properties": {
"exampleField": {
"stringValue": "Hello from transaction"
}
}
}
}
]
}

print("\n[COMMIT REQUEST BODY]")
print(json.dumps(commit_payload, indent=2))

commit_resp = requests.post(commit_url, json=commit_payload)
print("\n[COMMIT RESPONSE]")
print("Status Code:", commit_resp.status_code)
print("Body:", commit_resp.text)


if commit_resp.status_code == 200:
commit_data = commit_resp.json()
print("\nCommit Response JSON:")
print(json.dumps(commit_data, indent=2))
else:
print("Commit request failed.")

if __name__ == "__main__":
main()
```

### [REQUIRED] Steps to reproduce

When I use firestore emulator in datastore emulator mode to test the example script, it gives error while it works perfectly with Datasatore emulator

### [REQUIRED] Expected behavior

Datastore emulator's response is: (the last part of it)
```
[COMMIT RESPONSE]
Status Code: 200
Body: {
"mutationResults": [{
"version": "2"
}],
"commitVersion": "2"
}

Commit Response JSON:
{
"mutationResults": [
{
"version": "2"
}
],
"commitVersion": "2"
}
```
### [REQUIRED] Actual behavior

Firestore emulator's response is:

```
[COMMIT RESPONSE]
Status Code: 409
Body: {"error":{"code":409,"message":"Transaction lock timeout.","status":"ABORTED"}}
Commit request failed.
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.