## 3 System Architecture
### Data Layer  
At the bottom, we have the data layer centered on MongoDB Atlas. The database stores the rental data in the Rentals collection. MongoDB Atlas, being cloud-based, means the data is accessible from anywhere and can be scaled (sharded or replicated) as needed. The geospatial index of the Listings collection and text index are part of this layer, enabling complex queries to be executed directly by the database. The data layer also includes any static geospatial data, like the GeoJSON definitions of neighborhood boundaries used for the choropleth map, which can either be stored in the database or loaded as files on the server.  
Database Characteristics:  
* Total database size: 2.3 GB across 47,583 documents
* Average document size: 48.3 KB, including embedded arrays
* Index composition:  
* Compound geospatial index of location and price: 124 MB  
* Text index of title and description fields: 89 MB  
* Single-field indexes of scraped_at and rooms: 31 MB combined
* Working set size: 1.8 GB (fits entirely in RAM)  
Each document represents a single rental listing; thus, 47,583 documents equal 47,583 distinct accommodation places recorded since crawling began.  
Performance Benchmarking Methodology:  
Load testing followed industry-standard practices using Apache JMeter 5.5 (for Apache JMeter)/(Forest Hill, MD, USA) configured according to MongoDB's performance testing guidelines [11]. Tests simulated realistic user patterns based on access logs, with 70% read operations (searches/recommendations) and 30% writes (new listings/updates).  
Measured Performance Metrics:  
Response-time percentiles were calculated for over 1 million requests during peak load simulation:  
* Geospatial queries (finding listings within radius):  
* P50: 45 ms, P95: 89 ms, P99: 142 ms
* Aggregation pipelines (statistics generation):  
* P50: 123 ms, P95: 289 ms, P99: 456 ms  
The reinforcement-learning inference operates as a separate Python service using FastAPI 0.110.0 (Berlin, Germany), deployed on Vercel's serverless functions with 3 GB memory allocation. The model inference achieves a 34 ms latency for a batch size of 32, leveraging NumPy 1.26.4 (NumPy/SciPy developers via NumFOCUS)/(Austin, TX, USA) vectorization and scikit-learn's 1.4.0 optimized implementations. This performance surpasses the 100 ms threshold recommended for interactive applications by Nielsen [12].  
Given that we are using Vercel's serverless hosting, it features \"automatic concurrency scaling\"; this allows our FastAPI-based inference service to elastically adjust the number of running function instances in response to incoming request load. By default, each region can burst up to 1000 concurrent executions per 10 s window. Behind the scenes,Vercel's global edge network routes each inference request to the nearest available region, minimizing network latency and cold-start penalties. During traffic spikes, Vercel will preemptively spin up additional instances, then gracefully drain surplus capacity as load subsides, so that our 34 ms batch-32 latency remains stable under sudden demand surges. Finally, built-in observability via log drains and runtime logs gives real-time insight into invocation counts, execution durations, and concurrency events, enabling us to monitor, debug, and optimize our RL inference pipeline with minimal operational overhead [38].