|
| 1 | +# Redis OM Spring Multi-Tenant Demo |
| 2 | + |
| 3 | +This demo showcases the **Dynamic Indexing** features of Redis OM Spring, enabling multi-tenant SaaS applications with complete data isolation. |
| 4 | + |
| 5 | +## Features Demonstrated |
| 6 | + |
| 7 | +### 1. SpEL-based Dynamic Index Naming |
| 8 | + |
| 9 | +Use Spring Expression Language (SpEL) in `@IndexingOptions` annotations: |
| 10 | + |
| 11 | +```java |
| 12 | +@Document |
| 13 | +@IndexingOptions( |
| 14 | + indexName = "products_#{@tenantService.getCurrentTenant()}_idx", |
| 15 | + keyPrefix = "#{@tenantService.getCurrentTenant()}:products:" |
| 16 | +) |
| 17 | +public class Product { ... } |
| 18 | +``` |
| 19 | + |
| 20 | +### 2. Multi-Tenant Index Isolation |
| 21 | + |
| 22 | +Each tenant gets their own: |
| 23 | +- **Search Index**: `products_acme_idx`, `products_globex_idx` |
| 24 | +- **Key Prefix**: `acme:products:*`, `globex:products:*` |
| 25 | +- **Complete Data Isolation**: Searches only return tenant's own data |
| 26 | + |
| 27 | +### 3. Environment-based Configuration |
| 28 | + |
| 29 | +Configure indexes based on deployment environment: |
| 30 | + |
| 31 | +```java |
| 32 | +@IndexingOptions( |
| 33 | + indexName = "orders_#{@environment.getProperty('app.environment')}_idx" |
| 34 | +) |
| 35 | +``` |
| 36 | + |
| 37 | +### 4. Ephemeral Indexes with TTL |
| 38 | + |
| 39 | +Create temporary indexes that auto-delete: |
| 40 | + |
| 41 | +```java |
| 42 | +ephemeralIndexService.createEphemeralIndex( |
| 43 | + Product.class, |
| 44 | + "temp_analytics_idx", |
| 45 | + Duration.ofMinutes(30) |
| 46 | +); |
| 47 | +``` |
| 48 | + |
| 49 | +### 5. Index Migration & Aliasing |
| 50 | + |
| 51 | +Zero-downtime schema migrations using Redis aliases: |
| 52 | + |
| 53 | +```java |
| 54 | +// Blue-Green deployment |
| 55 | +indexer.createAlias("products_v2_idx", "products"); |
| 56 | +indexer.updateAlias("products_v1_idx", "products_v2_idx", "products"); |
| 57 | +``` |
| 58 | + |
| 59 | +### 6. Bulk Index Operations |
| 60 | + |
| 61 | +Manage all indexes at once: |
| 62 | + |
| 63 | +```java |
| 64 | +indexer.createIndexes(); // Create all registered indexes |
| 65 | +indexer.dropIndexes(); // Drop all managed indexes |
| 66 | +indexer.listIndexes(); // List all index names |
| 67 | +``` |
| 68 | + |
| 69 | +## Running the Demo |
| 70 | + |
| 71 | +### Prerequisites |
| 72 | + |
| 73 | +1. Redis Stack running on localhost:6379 |
| 74 | + ```bash |
| 75 | + docker run -p 6379:6379 redis/redis-stack |
| 76 | + ``` |
| 77 | + |
| 78 | +2. Build the project: |
| 79 | + ```bash |
| 80 | + ./gradlew build -x test |
| 81 | + ``` |
| 82 | + |
| 83 | +### Start the Application |
| 84 | + |
| 85 | +```bash |
| 86 | +./gradlew :demos:roms-multitenant:bootRun |
| 87 | +``` |
| 88 | + |
| 89 | +Watch the console for a visual walkthrough of all features! |
| 90 | + |
| 91 | +## REST API |
| 92 | + |
| 93 | +### Product Operations (Tenant-Scoped) |
| 94 | + |
| 95 | +```bash |
| 96 | +# Create a product for tenant "acme" |
| 97 | +curl -X POST http://localhost:8080/api/tenants/acme/products \ |
| 98 | + -H "Content-Type: application/json" \ |
| 99 | + -d '{ |
| 100 | + "name": "Widget Pro", |
| 101 | + "description": "Professional grade widget", |
| 102 | + "category": "Electronics", |
| 103 | + "sku": "WDG-PRO-001", |
| 104 | + "price": 299.99, |
| 105 | + "stockQuantity": 50, |
| 106 | + "active": true |
| 107 | + }' |
| 108 | + |
| 109 | +# List products for tenant "acme" |
| 110 | +curl http://localhost:8080/api/tenants/acme/products |
| 111 | + |
| 112 | +# Search products |
| 113 | +curl "http://localhost:8080/api/tenants/acme/products/search?q=widget" |
| 114 | + |
| 115 | +# Find by category |
| 116 | +curl http://localhost:8080/api/tenants/acme/products/category/Electronics |
| 117 | + |
| 118 | +# Find by price range |
| 119 | +curl "http://localhost:8080/api/tenants/acme/products/price-range?minPrice=100&maxPrice=500" |
| 120 | +``` |
| 121 | + |
| 122 | +### Admin Operations |
| 123 | + |
| 124 | +```bash |
| 125 | +# List all managed indexes |
| 126 | +curl http://localhost:8080/api/admin/indexes |
| 127 | + |
| 128 | +# Create all registered indexes |
| 129 | +curl -X POST http://localhost:8080/api/admin/indexes/create-all |
| 130 | + |
| 131 | +# Create index for specific tenant |
| 132 | +curl -X POST http://localhost:8080/api/admin/tenants/acme/indexes |
| 133 | + |
| 134 | +# Check if tenant index exists |
| 135 | +curl http://localhost:8080/api/admin/tenants/acme/indexes/exists |
| 136 | + |
| 137 | +# Create ephemeral index (60 second TTL) |
| 138 | +curl -X POST http://localhost:8080/api/admin/indexes/ephemeral \ |
| 139 | + -H "Content-Type: application/json" \ |
| 140 | + -d '{"indexName": "temp_batch_idx", "ttlSeconds": 60}' |
| 141 | + |
| 142 | +# Create index alias |
| 143 | +curl -X POST http://localhost:8080/api/admin/indexes/products_acme_idx/alias/products |
| 144 | + |
| 145 | +# Remove index alias |
| 146 | +curl -X DELETE http://localhost:8080/api/admin/indexes/products_acme_idx/alias/products |
| 147 | +``` |
| 148 | + |
| 149 | +## Multi-Tenant Usage Pattern |
| 150 | + |
| 151 | +```java |
| 152 | +@Service |
| 153 | +public class ProductService { |
| 154 | + |
| 155 | + @Autowired |
| 156 | + private TenantService tenantService; |
| 157 | + |
| 158 | + @Autowired |
| 159 | + private ProductRepository productRepository; |
| 160 | + |
| 161 | + public List<Product> getProductsForTenant(String tenantId) { |
| 162 | + tenantService.setCurrentTenant(tenantId); |
| 163 | + try { |
| 164 | + return productRepository.findAll(); |
| 165 | + } finally { |
| 166 | + tenantService.clearTenant(); |
| 167 | + } |
| 168 | + } |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +## Configuration |
| 173 | + |
| 174 | +### application.yaml |
| 175 | + |
| 176 | +```yaml |
| 177 | +spring: |
| 178 | + data: |
| 179 | + redis: |
| 180 | + host: localhost |
| 181 | + port: 6379 |
| 182 | + |
| 183 | +app: |
| 184 | + environment: development # Used in environment-based index names |
| 185 | + version: 1.0.0 |
| 186 | +``` |
| 187 | +
|
| 188 | +## Key Classes |
| 189 | +
|
| 190 | +| Class | Description | |
| 191 | +|-------|-------------| |
| 192 | +| `TenantService` | ThreadLocal tenant context management | |
| 193 | +| `Product` | Entity with dynamic index annotations | |
| 194 | +| `ProductController` | Tenant-scoped REST operations | |
| 195 | +| `AdminController` | Index management endpoints | |
| 196 | +| `DemoRunner` | Visual feature walkthrough | |
| 197 | + |
| 198 | +## Learn More |
| 199 | + |
| 200 | +- [Dynamic Indexing Design Document](https://github.com/redis/redis-om-spring/wiki/Dynamic-Indexing-Feature-Design) |
| 201 | +- [Redis OM Spring Documentation](https://github.com/redis/redis-om-spring) |
0 commit comments