Cloud Datastore is designed to scale horizontally by distributing records across multiple server nodes. However, this architecture introduces trade-offs: queries are only eventually consistent by default. To achieve strong consistency (guaranteeing that updates are visible immediately), Datastore uses Entity Groups defined by Ancestor Keys. But if too many write operations target the same Entity Group, you will create a database hotspot, leading to transaction timeouts.
By balancing ancestor key designs with composite keys, we can build high-performance, consistent databases.
1. Modeling Ancestor Keys for Transactional Security
An ancestor path links child entities to a parent root. This ensures that database operations inside the tree can run within secure transactions:
# ancestor_mapping.py
from google.cloud import ndb
class TenantAccount(ndb.Model):
company_name = ndb.StringProperty()
class UserProfile(ndb.Model):
email = ndb.StringProperty()
role = ndb.StringProperty()
def save_user_with_ancestor(tenant_id, email, role):
# Establish ancestor key path
parent_key = ndb.Key(TenantAccount, tenant_id)
# Define child key parent
user_key = ndb.Key(UserProfile, email, parent=parent_key)
user = UserProfile(key=user_key, email=email, role=role)
user.put()
2. Managing the 1-Write-Per-Second Limit
Entity groups are limited to approximately one write operation per second. To avoid hotspots, we do not place all application records under a single root ancestor. Instead, we divide ancestor roots by company or tenant boundaries, keeping write queues running fast.