Skip to content

Commit 16917e4

Browse files
GregoryTravisjdunkerley
authored andcommitted
Clear cache on reload (#11673)
(cherry picked from commit 9e00b9d)
1 parent 6d24ed0 commit 16917e4

File tree

9 files changed

+135
-40
lines changed

9 files changed

+135
-40
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
- [Added `Table.input` allowing creation of typed tables from vectors of data,
109109
including auto parsing text columns.][11562]
110110
- [Enhance Managed_Resource to allow implementation of in-memory caches][11577]
111+
- [The reload button clears the HTTP cache.][11673]
111112

112113
[11235]: https://github.com/enso-org/enso/pull/11235
113114
[11255]: https://github.com/enso-org/enso/pull/11255
@@ -116,6 +117,7 @@
116117
[11490]: https://github.com/enso-org/enso/pull/11490
117118
[11562]: https://github.com/enso-org/enso/pull/11562
118119
[11577]: https://github.com/enso-org/enso/pull/11577
120+
[11673]: https://github.com/enso-org/enso/pull/11673
119121

120122
#### Enso Language & Runtime
121123

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Standard.Base.Error.Error
2+
import Standard.Base.Meta
3+
import Standard.Base.Nothing.Nothing
4+
import Standard.Base.Runtime.Managed_Resource.Managed_Resource
5+
import Standard.Base.Runtime.Ref.Ref
6+
from Standard.Base.Data.Boolean import Boolean, True, False
7+
8+
## PRIVATE
9+
This is used by ReloadDetector.java to create a `Managed_Resource` that is
10+
garbage collected when the reload button is pressed.
11+
12+
The managed resource contains a Ref containing a 0 (the value is
13+
unimportant). When the reload button is pressed, the ref is removed and
14+
attempting to access it using `with` throws an `Uninitialized_State`. When
15+
the `Uninitialized_State` is detected, it indicates that the reload has been
16+
initiated.
17+
type Reload_Detector
18+
private Value mr:Managed_Resource
19+
20+
new -> Reload_Detector =
21+
mr = Managed_Resource.register (Ref.new 1) (x-> Nothing) True
22+
Reload_Detector.Value mr
23+
24+
has_reload_occurred self =
25+
self.mr.has_been_finalized
26+
27+
## PRIVATE
28+
simulate_reload_test_only reload_detector = reload_detector.mr.finalize
29+
30+
## PRIVATE
31+
create_reload_detector = Reload_Detector.new

distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Managed_Resource.enso

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
## An API for manual resource management.
22

33
import project.Any.Any
4+
import project.Errors.Common.Uninitialized_State
5+
import project.Meta
46
import project.Nothing.Nothing
5-
from project.Data.Boolean import Boolean, False
7+
from project.Data.Boolean import Boolean, True, False
68

79
## Resource provides an API for manual management of computation resources.
810

@@ -90,5 +92,15 @@ type Managed_Resource
9092
take : Any
9193
take self = @Builtin_Method "Managed_Resource.take"
9294

95+
## PRIVATE
96+
ADVANCED
97+
98+
Returns true iff the resource has been collected by the engine, false
99+
otherwise. If `with` throws any other error, it is propagated.
100+
has_been_finalized : Boolean
101+
has_been_finalized self -> Boolean = self.with x->
102+
if x.is_error.not then False else
103+
if x.catch.is_a Uninitialized_State then True else x
104+
93105
register_builtin r fn sys:Boolean = @Builtin_Method "Managed_Resource.register_builtin"
94106
with_builtin r fn = @Builtin_Method "Managed_Resource.with_builtin"

engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/command/RecomputeContextCmd.scala

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.enso.interpreter.instrument.{
1111
}
1212
import org.enso.interpreter.instrument.execution.RuntimeContext
1313
import org.enso.interpreter.instrument.job.{EnsureCompiledJob, ExecuteJob}
14+
import org.enso.interpreter.runtime.EnsoContext
1415
import org.enso.polyglot.runtime.Runtime.Api
1516
import org.enso.polyglot.runtime.Runtime.Api.RequestId
1617

@@ -42,6 +43,10 @@ class RecomputeContextCmd(
4243
ec: ExecutionContext
4344
): Future[Boolean] = {
4445
Future {
46+
EnsoContext
47+
.get(null)
48+
.getResourceManager()
49+
.scheduleFinalizationOfSystemReferences();
4550
ctx.jobControlPlane.abortJobs(
4651
request.contextId,
4752
"recompute context",

std-bits/base/src/main/java/org/enso/base/cache/LRUCache.java

+16
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public class LRUCache<M> {
7373
/** Used to get the current free disk space; mockable. */
7474
private final DiskSpaceGetter diskSpaceGetter;
7575

76+
/** Used to clear the cache on reload. */
77+
private final ReloadDetector reloadDetector = new ReloadDetector();
78+
7679
public LRUCache() {
7780
this(LRUCacheSettings.getDefault(), new NowGetter(), new DiskSpaceGetter());
7881
}
@@ -89,6 +92,8 @@ public LRUCache(LRUCacheSettings settings, NowGetter nowGetter, DiskSpaceGetter
8992
*/
9093
public CacheResult<M> getResult(ItemBuilder<M> itemBuilder)
9194
throws IOException, InterruptedException, ResponseTooLargeException {
95+
clearOnReload();
96+
9297
String cacheKey = itemBuilder.makeCacheKey();
9398

9499
try {
@@ -221,6 +226,12 @@ public void clear() {
221226
removeCacheEntriesByPredicate(e -> true);
222227
}
223228

229+
private void clearOnReload() {
230+
if (reloadDetector.hasReloadOccurred()) {
231+
clear();
232+
}
233+
}
234+
224235
/** Remove all cache entries (and their cache files) that match the predicate. */
225236
private void removeCacheEntriesByPredicate(Predicate<CacheEntry<M>> predicate) {
226237
List<Map.Entry<String, CacheEntry<M>>> toRemove =
@@ -352,6 +363,11 @@ public LRUCacheSettings getSettings() {
352363
return settings;
353364
}
354365

366+
/** Public for testing. */
367+
public void simulateReloadTestOnly() {
368+
reloadDetector.simulateReloadTestOnly();
369+
}
370+
355371
private record CacheEntry<M>(File responseData, M metadata, long size, ZonedDateTime expiry) {}
356372

357373
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.enso.base.cache;
2+
3+
import org.enso.base.polyglot.EnsoMeta;
4+
import org.graalvm.polyglot.Value;
5+
6+
/**
7+
* Detects that the reload button has been pressed.
8+
*
9+
* <p>.hasReloadOccurred() returns true if the reload button was pressed since the last call to
10+
* .hasReloadOccurred().
11+
*
12+
* <p>This uses a `Managed_Resource` (created in eval'd Enso code) that is cleared on reload.
13+
*/
14+
public class ReloadDetector {
15+
private Value ensoReloadDetector;
16+
17+
public ReloadDetector() {
18+
resetEnsoReloadDetector();
19+
}
20+
21+
public boolean hasReloadOccurred() {
22+
var reloadHasOccurred = ensoReloadDetector.invokeMember("has_reload_occurred").asBoolean();
23+
if (reloadHasOccurred) {
24+
resetEnsoReloadDetector();
25+
}
26+
return reloadHasOccurred;
27+
}
28+
29+
private void resetEnsoReloadDetector() {
30+
ensoReloadDetector =
31+
EnsoMeta.callStaticModuleMethod(
32+
"Standard.Base.Network.Reload_Detector", "create_reload_detector");
33+
}
34+
35+
void simulateReloadTestOnly() {
36+
EnsoMeta.callStaticModuleMethod(
37+
"Standard.Base.Network.Reload_Detector", "simulate_reload_test_only", ensoReloadDetector);
38+
}
39+
}

std-bits/base/src/main/java/org/enso/base/enso_cloud/EnsoSecretHelper.java

+4-39
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@
1919
import org.enso.base.net.URISchematic;
2020
import org.enso.base.net.URIWithSecrets;
2121
import org.graalvm.collections.Pair;
22-
import org.graalvm.polyglot.Context;
23-
import org.graalvm.polyglot.Value;
2422

2523
/** Makes HTTP requests with secrets in either header or query string. */
2624
public final class EnsoSecretHelper extends SecretValueResolver {
27-
private static Value cache;
25+
private static EnsoHTTPResponseCache cache;
2826

2927
/** Gets a JDBC connection resolving EnsoKeyValuePair into the properties. */
3028
public static Connection getJDBCConnection(
@@ -179,43 +177,10 @@ public EnsoHttpResponse reconstructResponseFromCachedStream(
179177
}
180178

181179
public static EnsoHTTPResponseCache getOrCreateCache() {
182-
if (getCache() instanceof EnsoHTTPResponseCache httpCache) {
183-
return httpCache;
184-
} else {
185-
var module =
186-
Context.getCurrent()
187-
.eval(
188-
"enso",
189-
"""
190-
import Standard.Base.Runtime.Managed_Resource.Managed_Resource
191-
import Standard.Base.Data.Boolean.Boolean
192-
193-
type Cache
194-
private Value ref:Managed_Resource
195-
196-
new obj -> Cache =
197-
on_finalize _ = 0
198-
ref = Managed_Resource.register obj on_finalize Boolean.True
199-
Cache.Value ref
200-
201-
get self = self.ref.with (r->r)
202-
""");
203-
var cacheNew = module.invokeMember("eval_expression", "Cache.new");
204-
var httpCache = new EnsoHTTPResponseCache();
205-
cache = cacheNew.execute(httpCache);
206-
return httpCache;
207-
}
208-
}
209-
210-
public static EnsoHTTPResponseCache getCache() {
211-
var c = cache instanceof Value v ? v.invokeMember("get") : null;
212-
if (c != null
213-
&& c.isHostObject()
214-
&& c.asHostObject() instanceof EnsoHTTPResponseCache httpCache) {
215-
return httpCache;
216-
} else {
217-
return null;
180+
if (cache == null) {
181+
cache = new EnsoHTTPResponseCache();
218182
}
183+
return cache;
219184
}
220185

221186
private static final Comparator<Pair<String, String>> headerNameComparator =

test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ add_specs suite_builder = suite_builder.group "Managed_Resource" group_builder->
9595

9696
# finalizes the resource
9797
mr.finalize
98+
mr.has_been_finalized . should_be_true
9899
builder.append "Finalized:"+mr.to_text
99100

100101
# operation on finalized resource

test/Table_Tests/src/IO/Fetch_Spec.enso

+24
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ add_specs suite_builder =
172172
lru_cache = LRUCache.new
173173
with_lru_cache lru_cache action
174174

175+
fake_reload =
176+
EnsoSecretHelper.getOrCreateCache.getLRUCache.simulateReloadTestOnly
177+
175178
url0 = base_url_with_slash+'test_download?max-age=16&length=10'
176179
url1 = base_url_with_slash+'test_download?max-age=16&length=20'
177180
url_post = base_url_with_slash + "post"
@@ -541,6 +544,27 @@ add_specs suite_builder =
541544
Test_Environment.unsafe_with_environment_override "ENSO_LIB_HTTP_CACHE_MAX_TOTAL_CACHE_LIMIT" "101%" <|
542545
LRUCache.new . getSettings . getTotalCacheLimit . should_equal (TotalCacheLimit.Percentage.new 0.2)
543546

547+
group_builder.specify "Cache should be cleared when a reload is detected" <|
548+
HTTP.fetch base_url_with_slash+'test_download?length=10'
549+
HTTP.fetch base_url_with_slash+'test_download?length=11'
550+
HTTP.fetch base_url_with_slash+'test_download?length=12'
551+
get_num_response_cache_entries . should_equal 3
552+
553+
fake_reload
554+
555+
get_num_response_cache_entries . should_equal 3 # Cleaning is not triggered until the next request
556+
HTTP.fetch base_url_with_slash+'test_download?length=10'
557+
get_num_response_cache_entries . should_equal 1
558+
HTTP.fetch base_url_with_slash+'test_download?length=14'
559+
HTTP.fetch base_url_with_slash+'test_download?length=15'
560+
get_num_response_cache_entries . should_equal 3
561+
562+
fake_reload
563+
564+
get_num_response_cache_entries . should_equal 3 # Cleaning is not triggered until the next request
565+
HTTP.fetch base_url_with_slash+'test_download?length=16'
566+
get_num_response_cache_entries . should_equal 1
567+
544568
group_builder.specify "Reissues the request if the cache file disappears" pending=pending_has_url <| Test.with_retries <|
545569
with_default_cache <|
546570
url = base_url_with_slash+'test_download?max-age=16&length=10'

0 commit comments

Comments
 (0)