SQLAlchemy Hybrid Properties for Computed Tenant Metrics: Avoiding SELECT N+1 When Aggregating AI Feature Usage Across Multi-Tenant Hierarchies A developer at CitizenApp solved a performance issue where a single dashboard query was generating 47,000 database calls by moving tenant metric computation from Python to PostgreSQL using SQLAlchemy's hybrid properties. The original approach of computing AI feature adoption rates in application code created a SELECT N+1 problem, with 1,001 queries for 1,000 tenants. By implementing the `hybrid_property` decorator with a SQL expression, the developer pushed the aggregation work to the database layer, eliminating the connection pool exhaustion that occurred under 500 concurrent users. I burned three weeks of performance optimization on CitizenApp before realizing the problem wasn't our FastAPI endpoints or React rendering—it was a single dashboard query that spawned 47,000 database calls. The culprit? Computing tenant metrics in Python instead of letting SQLAlchemy push the work to PostgreSQL. Most teams fall into this trap because it feels easier. You load tenants, loop through their feature usage, calculate adoption rates in memory. The code reads naturally. It works in development. Then production hits 500 concurrent users and your database connection pool evaporates. I'm going to show you why SQLAlchemy's hybrid property decorator exists, why it's essential for multi-tenant systems, and how to use it to move computation from your application layer to the database where it scales. Let's say you're tracking AI feature usage across a multi-tenant hierarchy. Your data model looks roughly like this: python from sqlalchemy import Column, Integer, String, ForeignKey, DateTime, func from sqlalchemy.orm import relationship from datetime import datetime class Tenant Base : tablename = "tenants" id = Column Integer, primary key=True name = Column String, nullable=False parent id = Column Integer, ForeignKey "tenants.id" , nullable=True children = relationship "Tenant", remote side= id , backref="parent" ai features = relationship "AIFeatureUsage", back populates="tenant" class AIFeatureUsage Base : tablename = "ai feature usage" id = Column Integer, primary key=True tenant id = Column Integer, ForeignKey "tenants.id" , nullable=False feature name = Column String, nullable=False inference cost = Column Float, default=0.0 created at = Column DateTime, default=datetime.utcnow tenant = relationship "Tenant", back populates="ai features" Your dashboard endpoint loads all tenants and computes adoption rate how many of your 9 AI features each tenant has used : FastAPI endpoint — WRONG APPROACH @app.get "/tenants/metrics" async def get tenant metrics session: Session = Depends get session : tenants = session.query Tenant .all 1 query metrics = for tenant in tenants: 1 query per tenant to count features feature count = session.query AIFeatureUsage \ .filter AIFeatureUsage.tenant id == tenant.id \ .distinct AIFeatureUsage.feature name \ .count adoption rate = feature count / 9 metrics.append { "tenant id": tenant.id, "adoption rate": adoption rate } return metrics With 100 tenants, this is 101 queries. With 1,000 tenants? 1,001 queries. The dashboard becomes unusable. SQLAlchemy's hybrid property lets you define computed attributes that work in two contexts: Here's the corrected approach: python from sqlalchemy.ext.hybrid import hybrid property from sqlalchemy import and class Tenant Base : tablename = "tenants" id = Column Integer, primary key=True name = Column String, nullable=False parent id = Column Integer, ForeignKey "tenants.id" , nullable=True children = relationship "Tenant", remote side= id , backref="parent" ai features = relationship "AIFeatureUsage", back populates="tenant" @hybrid property def feature adoption rate self - float: """ Python-side: Count distinct features used by this tenant. """ if not self.ai features: return 0.0 distinct features = len set f.feature name for f in self.ai features return distinct features / 9 @feature adoption rate.expression @classmethod def feature adoption rate cls : """ SQL-side: Compute adoption rate as a subquery. This runs in the database, not in Python. """ from sqlalchemy.sql import select, func as sql func feature count = select sql func.count sql func.distinct AIFeatureUsage.feature name .where AIFeatureUsage.tenant id == cls.id .correlate cls .scalar subquery return feature count / 9 Now your endpoint becomes: python @app.get "/tenants/metrics" async def get tenant metrics session: Session = Depends get session : tenants = session.query Tenant .add columns Tenant.feature adoption rate.label "adoption rate" .all return { "tenant id": t.id, "adoption rate": t.adoption rate } for t in tenants This produces one query —a single JOIN that computes adoption rates at the database layer. Real multi-tenant systems are hierarchical. Parent tenants inherit the aggregated usage of their children. Here's where hybrid properties shine: class Tenant Base : tablename = "tenants" id = Column Integer, primary key=True name = Column String, nullable=False parent id = Column Integer, ForeignKey "tenants.id" , nullable=True children = relationship "Tenant", remote side= id , backref="parent" ai features = relationship "AIFeatureUsage", back populates="tenant" @hybrid property def total inference cost self - float: """ Include costs from this tenant + all descendants. """ own cost = sum f.inference cost for f in self.ai features children cost = sum c.total inference cost for c in self.children return own cost + children cost @total inference cost.expression @classmethod def total inference cost cls : """ Recursive CTE in SQL PostgreSQL 12+ . """ from sqlalchemy import text Direct costs for this tenant direct = select func.coalesce func.sum AIFeatureUsage.inference cost , 0 .where AIFeatureUsage.tenant id == cls.id .correlate cls .scalar subquery Children costs you'd typically use a recursive CTE for large trees Simplified here for clarity children costs = select func.coalesce func.sum Tenant.total inference cost , 0 .where Tenant.parent id == cls.id .correlate cls .scalar subquery return direct + children costs Here's what burned me: hybrid properties don't always translate to SQL. Some expressions are too complex or use Python-only logic: python class Tenant Base : @hybrid property def permission inheritance depth self - int: """ Count how many levels deep in the hierarchy. This LOOKS like it should work... """ if self.parent: return 1 + self.parent.permission inheritance depth return 0 @permission inheritance depth.expression @classmethod def permission inheritance depth cls : This WILL FAIL for deep hierarchies—recursion limit hit Use PostgreSQL's WITH RECURSIVE instead pass Fix: For recursive hierarchies, use PostgreSQL CTEs directly: python from sqlalchemy import text, literal column @classmethod def permission inheritance depth expr cls : Raw SQL CTE—the only safe way for deep trees cte = text """ WITH RECURSIVE tenant depth AS SELECT id, parent id, 0 as depth FROM tenants WHERE id = :tenant id UNION ALL SELECT t.id, t.parent id, td.depth + 1 FROM tenants t JOIN tenant depth td ON t.id = td.parent id SELECT MAX depth FROM tenant depth """ return cte CitizenApp's dashboard queries 9 AI features across a 3-level tenant hierarchy. Without hybrid properties, each metric was N+1: load tenants, load features per tenant, load children, compute costs, compute adoption. That's roughly 30 queries per user. With hybrid properties pushed to SQL: The pattern generalizes: any computed metric that depends on related data belongs in a hybrid property. Let your database do what it's designed for.