cds-caching 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +167 -3
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -93,6 +93,97 @@ For more control, you can specify additional options:
93
93
  }
94
94
  ```
95
95
 
96
+ ### Real-World Usage and Deployment
97
+
98
+
99
+ #### Storage Options
100
+
101
+ cds-caching provides two storage options:
102
+
103
+ ##### In-Memory Cache (for small-scale use)
104
+ - Simple and fast, but not persistent
105
+ - Not suitable for production since Node.js runtime memory is limited
106
+ - Data is lost when the application restarts
107
+
108
+ ##### Redis Cache (recommended for production)
109
+ - Persistent and supports distributed caching
110
+ - Works across multiple app instances, making it ideal for scalable applications
111
+ - Available on SAP BTP via hyperscaler options (e.g., AWS, Azure, Google Cloud)
112
+ - Even trial accounts provide Redis access
113
+
114
+ #### Development Setup
115
+
116
+ ##### Running Redis Locally via Docker
117
+ For local development, Redis can be quickly set up using Docker. A simple docker-compose configuration provides a lightweight caching environment:
118
+
119
+ 1. Create a `docker-compose.yml` file:
120
+ ```yaml
121
+ services:
122
+ redis:
123
+ image: redis:latest
124
+ container_name: local-redis
125
+ ports:
126
+ - "6379:6379"
127
+ ```
128
+
129
+ 2. Run Redis with:
130
+ ```bash
131
+ docker compose up -d
132
+ ```
133
+
134
+ 3. Modify the `package.json` configuration to connect to the local Redis instance:
135
+ ```json
136
+ {
137
+ "cds": {
138
+ "requires": {
139
+ "caching": {
140
+ "impl": "cds-caching",
141
+ "namespace": "myCache",
142
+ "store": "redis",
143
+ "[development]": {
144
+ "credentials": {
145
+ "host": "localhost",
146
+ "port": 6379
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ Now, caching will be handled by Redis instead of in-memory storage during development.
156
+
157
+ #### Production Deployment on SAP BTP
158
+
159
+ ![Redis on SAP BTP](./docs/caching-btp.png)
160
+
161
+ For production deployments on SAP BTP, Redis can be provisioned as a managed service through the Redis on SAP BTP hyperscaler option. An instance can be provisioned via trial or even as a Free Tier to explore the service. However, for production scenarios the size of the Redis instance should match your caching requirements.
162
+
163
+ To bind Redis to your CAP application on SAP BTP, add the following configuration in `mta.yaml`. This will automatically create the service instance and bind your application to it. Since the credentials will automatically be fetched by CAP, make sure to maintain the service-tags to match the kind property of your cds-caching service(s) in the package.json:
164
+
165
+ ```yaml
166
+ modules:
167
+ - name: cap-app-srv
168
+ # ... other module configuration ...
169
+ requires:
170
+ - name: redis-cache
171
+
172
+ resources:
173
+ - name: redis-cache
174
+ type: org.cloudfoundry.managed-service
175
+ parameters:
176
+ service: redis-cache
177
+ service-plan: trial
178
+ service-tags:
179
+ # Must match the kind property in the package.json
180
+ - cds-caching
181
+ ```
182
+
183
+ > 👉 **Tip**: There is a detailed [blog series on Redis in SAP BTP](https://community.sap.com/t5/technology-blogs-by-sap/redis-on-sap-btp-understanding-service-entitlements-and-metrics/ba-p/13738371) explaining how to set up Redis and connect via SSH for local/hybrid testing, as this is by default not possible.
184
+
185
+
186
+
96
187
  ### Usage Patterns
97
188
 
98
189
  The caching service provides a flexible API for caching data in CAP applications. Here are the key usage patterns:
@@ -266,7 +357,49 @@ const result = await cache.exec("key", async () => {
266
357
  })
267
358
  ```
268
359
 
269
- #### 2. Tag-Based
360
+ #### 2. Key-Based
361
+
362
+ Key-based invalidation is a way to invalidate cache entries based on a specific key.
363
+
364
+ ```javascript
365
+ await cache.delete("key")
366
+ ```
367
+
368
+ Keys are critical for cache invalidation. To allow custom key management, you can override the auto-generated key. This option is available for all essential methods (e.g cache.set, cache.run, cache.send, cache.createKey) and for the annotations.
369
+
370
+ ```javascript
371
+ // No key override given, string will just be used as keys
372
+ await cache.set('key', 'value') // key: key
373
+
374
+ // No key override given, objects will be smartly hashed
375
+ await cache.set(SELECT.from(Foo)) // key: bd3f3690d3e96a569bd89d9e207a89af
376
+
377
+ // Automatically build the key for retrieval/deletion
378
+ cache.createKey(SELECT.from(Foo)) // key: bd3f3690d3e96a569bd89d9e207a89af
379
+
380
+ // Override and use your own key based on a fixed value
381
+ await cache.set(SELECT.from(Foo, 1), { key: { value: "foo:1" } })
382
+
383
+ // Override and only for requests, use request context information
384
+ await cache.run(req, remoteService, { key: { template: "mykey:{tenant}:{user}:{locale}:{hash}" } })
385
+
386
+ // This requests will be cached for all users and for each locale
387
+ await cache.set(req, remoteService, { key: { template: "mykey:{user}:{locale}:{hash}" } })
388
+ ```
389
+
390
+ Overriding keys support the following configuration options:
391
+ - `value` – generates a static value
392
+ - `prefix` – will add this piece at the beginning
393
+ - `suffix` - will ad this piece at the end
394
+ - `template` - will set a value filled with placeholders, available placeholders are (only relevant for cds.Requests):
395
+ - `{user}`: The current user
396
+ - `{tenant}`: The current tenant
397
+ - `{locale}`: The current locale
398
+ - `{hash}`: The hash of the request query/params/data/path/etc.
399
+
400
+ With well-structured keys, invalidating cache entries becomes a lot easier. However, for more complex scenarios tags provide an even more effective solution, as tags can automatically be created based on the cached data.
401
+
402
+ #### 3. Tag-Based
270
403
 
271
404
  Tags are a way to invalidate cache entries based on a specific tag. Tags need to be provided explicitly when storing a value in the cache and are supported for all cache methods (e.g. `set`, `run`, `send`, `wrap`, `exec`).
272
405
  Tags can be provided as an array of strings or as an array of objects with the following properties:
@@ -434,7 +567,8 @@ The following properties are accepted:
434
567
  | Property | Description | Example |
435
568
  | ------------- | ------------- | ----------
436
569
  | ttl | Time-to-live in milliseconds. | `1000`
437
- | tags | Array of tags to associate with the value. Tags can be dynamic based on the given value (see chapter Cache Invalidation Strategies) | `[{template: 'user-{user}', value: '123'}]`
570
+ | key | Key override for the cache for full control over the key management (see chapter Cache Invalidation Strategies) | `{template: 'user-{user}', value: '123'}`
571
+ | tags | Array of tags to associate with the value. Tags can be dynamic based on the stored cache data (see chapter Cache Invalidation Strategies) | `[{template: 'user-{user}', value: '123'}]`
438
572
 
439
573
  ---
440
574
 
@@ -572,7 +706,7 @@ The result of the function.
572
706
 
573
707
  ---
574
708
 
575
- ### `await cache.iterator()`
709
+ ### `await cache.iterator() : AsyncIterator<{ key: string, value: { value: any, tags: string[], timestamp: number } }>`
576
710
 
577
711
  Returns an iterator over all cache entries.
578
712
 
@@ -582,6 +716,36 @@ An iterator over all cache entries.
582
716
 
583
717
  ---
584
718
 
719
+ ### `await cache.tags(key: any) : string[]`
720
+
721
+ Returns the tags for a given key.
722
+
723
+ #### `key: any`
724
+
725
+ The key to get the tags for. The key handling is the same as for the `createKey` method.
726
+
727
+ #### Returns
728
+
729
+ An array of tags. If the key does not exist, an empty array is returned.
730
+
731
+ ---
732
+
733
+ ### `await cache.metadata(key: any) : { tags: string[], timestamp: number } | undefined`
734
+
735
+ Returns the metadata for a given key.
736
+
737
+ #### `key: any`
738
+
739
+ The key to get the metadata for. The key handling is the same as for the `createKey` method.
740
+
741
+ #### Returns
742
+
743
+ An object containing the metadata for the given key or `undefined` if the key does not exist. The metadata object contains the following properties:
744
+
745
+ - `tags`: An array of tags.
746
+ - `timestamp`: The timestamp of the cache entry.
747
+
748
+ ---
585
749
 
586
750
  ### Contributing
587
751
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cds-caching",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "A caching plugin for SAP CAP applications supporting Redis",
5
5
  "repository": {
6
6
  "type": "git",