This example demonstrates a sophisticated Trusted plugin for a multi-tenant SaaS application. It processes events from other plugins, stores them in tenant-isolated databases, and uses a Scheduler to generate per-tenant reports.
fromxcoreimportTrustedBase,okfromsqlalchemyimporttext,func,selectimportlogginglogger=logging.getLogger("xcore.analytics")classPlugin(TrustedBase):asyncdefon_load(self):self.db=self.get_service("db")self.cache=self.get_service("cache")self.scheduler=self.get_service("scheduler")asyncdefon_start(self):# (1) Event-Driven Data Collection@self.ctx.events.on("user.created")asyncdefon_user_signup(event):# Xcore automatically propagates the tenant_id from the event emitter!awaitself._record_event("signup",event.data)# (2) Tenant-Aware Scheduling# This job will be executed for EVERY tenant currently active.@self.scheduler.cron("0 0 * * *")asyncdefnightly_aggregation():awaitself._compute_daily_totals()asyncdef_record_event(self,event_type:str,data:dict):asyncwithself.db.session()assess:# Query is automatically scoped to the correct tenant schemaawaitsess.execute(text("INSERT INTO analytics_events (type, payload) VALUES (:t, :p)"),{"t":event_type,"p":str(data)})# Cache increment is also tenant-prefixed (e.g., "acme:stats:signup")awaitself.cache.incr(f"stats:{event_type}")asyncdef_compute_daily_totals(self):# Heavy aggregation logic...# The scheduler preserves the tenant context during execution.logger.info(f"Generating report for tenant: {self.ctx.tenant_id}")# ... aggregation logic ...asyncdefhandle(self,action,payload):ifaction=="get_dashboard":signups=awaitself.cache.get("stats:signup")returnok(dashboard={"signups":signupsor0})returnok()
One of Xcore's most powerful features is asynchronous context propagation.
- When the User Manager plugin emits user.created, it includes the current tenant_id.
- Xcore ensures that when this plugin's @events.on handler runs, self.ctx.tenant_id is correctly set to the emitter's tenant.
- This means your self.db and self.cache calls remain isolated without you passing IDs around manually.
Scheduled jobs are traditionally difficult to manage in multi-tenant systems. Xcore solves this by tracking which tenants have "active" plugins and spawning the job in the correct security context for each one.