0
# Caching System
1
2
Script compilation caching interface with configurable cache implementations for improved performance in repeated script execution scenarios. The caching system supports both in-memory and persistent storage strategies.
3
4
## Capabilities
5
6
### Compiled Scripts Cache Interface
7
8
Core interface for caching compiled JVM scripts with flexible storage and retrieval mechanisms.
9
10
```kotlin { .api }
11
/**
12
* Cache interface for compiled JVM scripts
13
* Provides storage and retrieval of compiled scripts to improve performance
14
*/
15
interface CompiledJvmScriptsCache {
16
/**
17
* Retrieve cached compiled script
18
* @param script Source code of the script
19
* @param scriptCompilationConfiguration Configuration used for compilation
20
* @return Cached compiled script or null if not found
21
*/
22
fun get(
23
script: SourceCode,
24
scriptCompilationConfiguration: ScriptCompilationConfiguration
25
): CompiledScript?
26
27
/**
28
* Store compiled script in cache
29
* @param compiledScript The compiled script to cache
30
* @param script Original source code
31
* @param scriptCompilationConfiguration Configuration used for compilation
32
*/
33
fun store(
34
compiledScript: CompiledScript,
35
script: SourceCode,
36
scriptCompilationConfiguration: ScriptCompilationConfiguration
37
)
38
39
/**
40
* No-operation cache implementation
41
* Does not store or retrieve any scripts
42
*/
43
object NoCache : CompiledJvmScriptsCache {
44
override fun get(
45
script: SourceCode,
46
scriptCompilationConfiguration: ScriptCompilationConfiguration
47
): CompiledScript? = null
48
49
override fun store(
50
compiledScript: CompiledScript,
51
script: SourceCode,
52
scriptCompilationConfiguration: ScriptCompilationConfiguration
53
) = Unit
54
}
55
}
56
```
57
58
**Usage Examples:**
59
60
```kotlin
61
import kotlin.script.experimental.api.*
62
import kotlin.script.experimental.jvm.*
63
64
// Custom in-memory cache implementation
65
class InMemoryScriptCache : CompiledJvmScriptsCache {
66
private val cache = mutableMapOf<String, CompiledScript>()
67
68
private fun generateCacheKey(
69
script: SourceCode,
70
config: ScriptCompilationConfiguration
71
): String {
72
return "${script.text.hashCode()}-${config.hashCode()}"
73
}
74
75
override fun get(
76
script: SourceCode,
77
scriptCompilationConfiguration: ScriptCompilationConfiguration
78
): CompiledScript? {
79
val key = generateCacheKey(script, scriptCompilationConfiguration)
80
return cache[key]
81
}
82
83
override fun store(
84
compiledScript: CompiledScript,
85
script: SourceCode,
86
scriptCompilationConfiguration: ScriptCompilationConfiguration
87
) {
88
val key = generateCacheKey(script, scriptCompilationConfiguration)
89
cache[key] = compiledScript
90
}
91
}
92
93
// Use cache in compilation configuration
94
val scriptCache = InMemoryScriptCache()
95
96
val compilationConfig = ScriptCompilationConfiguration {
97
jvm {
98
compilationCache = scriptCache
99
}
100
}
101
```
102
103
### Cache Configuration Property
104
105
Configuration property for specifying cache implementation in script compilation settings.
106
107
```kotlin { .api }
108
/**
109
* Property key for compiled scripts cache in compilation configuration
110
*/
111
val ScriptCompilationConfiguration.compilationCache: PropertiesCollection.Key<CompiledJvmScriptsCache>
112
```
113
114
**Usage Example:**
115
116
```kotlin
117
// Configure cache in compilation settings
118
val config = ScriptCompilationConfiguration {
119
compilationCache = InMemoryScriptCache()
120
// ... other configuration
121
}
122
123
// Use NoCache for no caching
124
val noCacheConfig = ScriptCompilationConfiguration {
125
compilationCache = CompiledJvmScriptsCache.NoCache
126
}
127
```
128
129
## Implementation Examples
130
131
### File-Based Persistent Cache
132
133
```kotlin
134
import java.io.File
135
import java.security.MessageDigest
136
137
class FileBasedScriptCache(private val cacheDirectory: File) : CompiledJvmScriptsCache {
138
139
init {
140
cacheDirectory.mkdirs()
141
}
142
143
private fun generateCacheKey(
144
script: SourceCode,
145
config: ScriptCompilationConfiguration
146
): String {
147
val content = "${script.text}-${config.toString()}"
148
val digest = MessageDigest.getInstance("SHA-256")
149
val hash = digest.digest(content.toByteArray())
150
return hash.joinToString("") { "%02x".format(it) }
151
}
152
153
private fun getCacheFile(key: String): File {
154
return File(cacheDirectory, "$key.cached")
155
}
156
157
override fun get(
158
script: SourceCode,
159
scriptCompilationConfiguration: ScriptCompilationConfiguration
160
): CompiledScript? {
161
val key = generateCacheKey(script, scriptCompilationConfiguration)
162
val cacheFile = getCacheFile(key)
163
164
return if (cacheFile.exists()) {
165
try {
166
// Deserialize cached script
167
deserializeCompiledScript(cacheFile.readBytes())
168
} catch (e: Exception) {
169
// Cache corruption, remove file
170
cacheFile.delete()
171
null
172
}
173
} else {
174
null
175
}
176
}
177
178
override fun store(
179
compiledScript: CompiledScript,
180
script: SourceCode,
181
scriptCompilationConfiguration: ScriptCompilationConfiguration
182
) {
183
val key = generateCacheKey(script, scriptCompilationConfiguration)
184
val cacheFile = getCacheFile(key)
185
186
try {
187
val serializedScript = (compiledScript as KJvmCompiledScript).toBytes()
188
cacheFile.writeBytes(serializedScript)
189
} catch (e: Exception) {
190
// Failed to cache, continue without caching
191
println("Warning: Failed to cache script: ${e.message}")
192
}
193
}
194
195
fun clearCache() {
196
cacheDirectory.listFiles()?.forEach { it.delete() }
197
}
198
199
fun getCacheSize(): Long {
200
return cacheDirectory.listFiles()?.sumOf { it.length() } ?: 0L
201
}
202
}
203
```
204
205
### LRU In-Memory Cache
206
207
```kotlin
208
import java.util.LinkedHashMap
209
210
class LRUScriptCache(private val maxSize: Int = 100) : CompiledJvmScriptsCache {
211
212
private val cache = object : LinkedHashMap<String, CompiledScript>(16, 0.75f, true) {
213
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<String, CompiledScript>): Boolean {
214
return size > maxSize
215
}
216
}
217
218
private fun generateCacheKey(
219
script: SourceCode,
220
config: ScriptCompilationConfiguration
221
): String {
222
return "${script.text.hashCode()}-${config.hashCode()}"
223
}
224
225
@Synchronized
226
override fun get(
227
script: SourceCode,
228
scriptCompilationConfiguration: ScriptCompilationConfiguration
229
): CompiledScript? {
230
val key = generateCacheKey(script, scriptCompilationConfiguration)
231
return cache[key]
232
}
233
234
@Synchronized
235
override fun store(
236
compiledScript: CompiledScript,
237
script: SourceCode,
238
scriptCompilationConfiguration: ScriptCompilationConfiguration
239
) {
240
val key = generateCacheKey(script, scriptCompilationConfiguration)
241
cache[key] = compiledScript
242
}
243
244
@Synchronized
245
fun clear() {
246
cache.clear()
247
}
248
249
@Synchronized
250
fun size(): Int = cache.size
251
252
@Synchronized
253
fun getStatistics(): CacheStatistics {
254
return CacheStatistics(
255
size = cache.size,
256
maxSize = maxSize,
257
hitRate = calculateHitRate()
258
)
259
}
260
}
261
262
data class CacheStatistics(
263
val size: Int,
264
val maxSize: Int,
265
val hitRate: Double
266
)
267
```
268
269
### Composite Cache Implementation
270
271
```kotlin
272
class CompositeScriptCache(
273
private val primaryCache: CompiledJvmScriptsCache,
274
private val secondaryCache: CompiledJvmScriptsCache
275
) : CompiledJvmScriptsCache {
276
277
override fun get(
278
script: SourceCode,
279
scriptCompilationConfiguration: ScriptCompilationConfiguration
280
): CompiledScript? {
281
// Try primary cache first
282
primaryCache.get(script, scriptCompilationConfiguration)?.let { return it }
283
284
// Try secondary cache
285
val result = secondaryCache.get(script, scriptCompilationConfiguration)
286
287
// If found in secondary, promote to primary
288
result?.let {
289
primaryCache.store(it, script, scriptCompilationConfiguration)
290
}
291
292
return result
293
}
294
295
override fun store(
296
compiledScript: CompiledScript,
297
script: SourceCode,
298
scriptCompilationConfiguration: ScriptCompilationConfiguration
299
) {
300
// Store in both caches
301
primaryCache.store(compiledScript, script, scriptCompilationConfiguration)
302
secondaryCache.store(compiledScript, script, scriptCompilationConfiguration)
303
}
304
}
305
306
// Usage: Fast in-memory cache with persistent backup
307
val compositeCache = CompositeScriptCache(
308
primaryCache = LRUScriptCache(maxSize = 50),
309
secondaryCache = FileBasedScriptCache(File("script-cache"))
310
)
311
```
312
313
### Time-Based Expiring Cache
314
315
```kotlin
316
import java.time.Instant
317
import java.time.Duration
318
import java.util.concurrent.ConcurrentHashMap
319
320
class ExpiringScriptCache(
321
private val ttl: Duration = Duration.ofHours(1)
322
) : CompiledJvmScriptsCache {
323
324
private data class CacheEntry(
325
val script: CompiledScript,
326
val timestamp: Instant
327
) {
328
fun isExpired(now: Instant): Boolean {
329
return Duration.between(timestamp, now) > ttl
330
}
331
}
332
333
private val cache = ConcurrentHashMap<String, CacheEntry>()
334
335
private fun generateCacheKey(
336
script: SourceCode,
337
config: ScriptCompilationConfiguration
338
): String {
339
return "${script.text.hashCode()}-${config.hashCode()}"
340
}
341
342
override fun get(
343
script: SourceCode,
344
scriptCompilationConfiguration: ScriptCompilationConfiguration
345
): CompiledScript? {
346
val key = generateCacheKey(script, scriptCompilationConfiguration)
347
val entry = cache[key]
348
val now = Instant.now()
349
350
return if (entry != null && !entry.isExpired(now)) {
351
entry.script
352
} else {
353
// Remove expired entry
354
cache.remove(key)
355
null
356
}
357
}
358
359
override fun store(
360
compiledScript: CompiledScript,
361
script: SourceCode,
362
scriptCompilationConfiguration: ScriptCompilationConfiguration
363
) {
364
val key = generateCacheKey(script, scriptCompilationConfiguration)
365
val entry = CacheEntry(compiledScript, Instant.now())
366
cache[key] = entry
367
}
368
369
fun cleanExpiredEntries() {
370
val now = Instant.now()
371
cache.entries.removeIf { (_, entry) -> entry.isExpired(now) }
372
}
373
}
374
```
375
376
### Configuration-Based Cache Factory
377
378
```kotlin
379
enum class CacheType {
380
NONE, IN_MEMORY, FILE_BASED, LRU, EXPIRING, COMPOSITE
381
}
382
383
data class CacheConfiguration(
384
val type: CacheType = CacheType.IN_MEMORY,
385
val maxSize: Int = 100,
386
val ttlHours: Long = 1,
387
val cacheDirectory: File? = null
388
)
389
390
object ScriptCacheFactory {
391
fun createCache(config: CacheConfiguration): CompiledJvmScriptsCache {
392
return when (config.type) {
393
CacheType.NONE -> CompiledJvmScriptsCache.NoCache
394
395
CacheType.IN_MEMORY -> InMemoryScriptCache()
396
397
CacheType.FILE_BASED -> {
398
val dir = config.cacheDirectory ?: File("script-cache")
399
FileBasedScriptCache(dir)
400
}
401
402
CacheType.LRU -> LRUScriptCache(config.maxSize)
403
404
CacheType.EXPIRING -> ExpiringScriptCache(Duration.ofHours(config.ttlHours))
405
406
CacheType.COMPOSITE -> CompositeScriptCache(
407
primaryCache = LRUScriptCache(config.maxSize),
408
secondaryCache = FileBasedScriptCache(
409
config.cacheDirectory ?: File("script-cache")
410
)
411
)
412
}
413
}
414
}
415
416
// Usage
417
val cacheConfig = CacheConfiguration(
418
type = CacheType.COMPOSITE,
419
maxSize = 200,
420
cacheDirectory = File("/tmp/kotlin-script-cache")
421
)
422
423
val cache = ScriptCacheFactory.createCache(cacheConfig)
424
425
val compilationConfig = ScriptCompilationConfiguration {
426
compilationCache = cache
427
}
428
```
429
430
## Best Practices
431
432
### Cache Key Generation
433
434
```kotlin
435
// Good: Include all relevant compilation factors
436
fun generateSecureCacheKey(
437
script: SourceCode,
438
config: ScriptCompilationConfiguration
439
): String {
440
val content = buildString {
441
append(script.text)
442
append(config.jvm.jvmTarget.value ?: "")
443
append(config.dependencies.joinToString { it.toString() })
444
append(config.jvm.jdkHome.value?.absolutePath ?: "")
445
}
446
447
return MessageDigest.getInstance("SHA-256")
448
.digest(content.toByteArray())
449
.joinToString("") { "%02x".format(it) }
450
}
451
```
452
453
### Cache Monitoring
454
455
```kotlin
456
class MonitoredScriptCache(
457
private val delegate: CompiledJvmScriptsCache,
458
private val metrics: CacheMetrics
459
) : CompiledJvmScriptsCache {
460
461
override fun get(
462
script: SourceCode,
463
scriptCompilationConfiguration: ScriptCompilationConfiguration
464
): CompiledScript? {
465
val result = delegate.get(script, scriptCompilationConfiguration)
466
if (result != null) {
467
metrics.recordHit()
468
} else {
469
metrics.recordMiss()
470
}
471
return result
472
}
473
474
override fun store(
475
compiledScript: CompiledScript,
476
script: SourceCode,
477
scriptCompilationConfiguration: ScriptCompilationConfiguration
478
) {
479
delegate.store(compiledScript, script, scriptCompilationConfiguration)
480
metrics.recordStore()
481
}
482
}
483
```