mirror of
https://github.com/enso-org/enso.git
synced 2024-12-18 19:41:32 +03:00
Clear cache on reload (#11673)
This commit is contained in:
parent
d79b4218fc
commit
9e00b9ddcc
@ -108,6 +108,7 @@
|
||||
- [Added `Table.input` allowing creation of typed tables from vectors of data,
|
||||
including auto parsing text columns.][11562]
|
||||
- [Enhance Managed_Resource to allow implementation of in-memory caches][11577]
|
||||
- [The reload button clears the HTTP cache.][11673]
|
||||
|
||||
[11235]: https://github.com/enso-org/enso/pull/11235
|
||||
[11255]: https://github.com/enso-org/enso/pull/11255
|
||||
@ -116,6 +117,7 @@
|
||||
[11490]: https://github.com/enso-org/enso/pull/11490
|
||||
[11562]: https://github.com/enso-org/enso/pull/11562
|
||||
[11577]: https://github.com/enso-org/enso/pull/11577
|
||||
[11673]: https://github.com/enso-org/enso/pull/11673
|
||||
|
||||
#### Enso Language & Runtime
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
import Standard.Base.Error.Error
|
||||
import Standard.Base.Meta
|
||||
import Standard.Base.Nothing.Nothing
|
||||
import Standard.Base.Runtime.Managed_Resource.Managed_Resource
|
||||
import Standard.Base.Runtime.Ref.Ref
|
||||
from Standard.Base.Data.Boolean import Boolean, True, False
|
||||
|
||||
## PRIVATE
|
||||
This is used by ReloadDetector.java to create a `Managed_Resource` that is
|
||||
garbage collected when the reload button is pressed.
|
||||
|
||||
The managed resource contains a Ref containing a 0 (the value is
|
||||
unimportant). When the reload button is pressed, the ref is removed and
|
||||
attempting to access it using `with` throws an `Uninitialized_State`. When
|
||||
the `Uninitialized_State` is detected, it indicates that the reload has been
|
||||
initiated.
|
||||
type Reload_Detector
|
||||
private Value mr:Managed_Resource
|
||||
|
||||
new -> Reload_Detector =
|
||||
mr = Managed_Resource.register (Ref.new 1) (x-> Nothing) True
|
||||
Reload_Detector.Value mr
|
||||
|
||||
has_reload_occurred self =
|
||||
self.mr.has_been_finalized
|
||||
|
||||
## PRIVATE
|
||||
simulate_reload_test_only reload_detector = reload_detector.mr.finalize
|
||||
|
||||
## PRIVATE
|
||||
create_reload_detector = Reload_Detector.new
|
@ -1,8 +1,10 @@
|
||||
## An API for manual resource management.
|
||||
|
||||
import project.Any.Any
|
||||
import project.Errors.Common.Uninitialized_State
|
||||
import project.Meta
|
||||
import project.Nothing.Nothing
|
||||
from project.Data.Boolean import Boolean, False
|
||||
from project.Data.Boolean import Boolean, True, False
|
||||
|
||||
## Resource provides an API for manual management of computation resources.
|
||||
|
||||
@ -90,5 +92,15 @@ type Managed_Resource
|
||||
take : Any
|
||||
take self = @Builtin_Method "Managed_Resource.take"
|
||||
|
||||
## PRIVATE
|
||||
ADVANCED
|
||||
|
||||
Returns true iff the resource has been collected by the engine, false
|
||||
otherwise. If `with` throws any other error, it is propagated.
|
||||
has_been_finalized : Boolean
|
||||
has_been_finalized self -> Boolean = self.with x->
|
||||
if x.is_error.not then False else
|
||||
if x.catch.is_a Uninitialized_State then True else x
|
||||
|
||||
register_builtin r fn sys:Boolean = @Builtin_Method "Managed_Resource.register_builtin"
|
||||
with_builtin r fn = @Builtin_Method "Managed_Resource.with_builtin"
|
||||
|
@ -11,6 +11,7 @@ import org.enso.interpreter.instrument.{
|
||||
}
|
||||
import org.enso.interpreter.instrument.execution.RuntimeContext
|
||||
import org.enso.interpreter.instrument.job.{EnsureCompiledJob, ExecuteJob}
|
||||
import org.enso.interpreter.runtime.EnsoContext
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.polyglot.runtime.Runtime.Api.RequestId
|
||||
|
||||
@ -42,6 +43,10 @@ class RecomputeContextCmd(
|
||||
ec: ExecutionContext
|
||||
): Future[Boolean] = {
|
||||
Future {
|
||||
EnsoContext
|
||||
.get(null)
|
||||
.getResourceManager()
|
||||
.scheduleFinalizationOfSystemReferences();
|
||||
ctx.jobControlPlane.abortJobs(
|
||||
request.contextId,
|
||||
"recompute context",
|
||||
|
@ -73,6 +73,9 @@ public class LRUCache<M> {
|
||||
/** Used to get the current free disk space; mockable. */
|
||||
private final DiskSpaceGetter diskSpaceGetter;
|
||||
|
||||
/** Used to clear the cache on reload. */
|
||||
private final ReloadDetector reloadDetector = new ReloadDetector();
|
||||
|
||||
public LRUCache() {
|
||||
this(LRUCacheSettings.getDefault(), new NowGetter(), new DiskSpaceGetter());
|
||||
}
|
||||
@ -89,6 +92,8 @@ public class LRUCache<M> {
|
||||
*/
|
||||
public CacheResult<M> getResult(ItemBuilder<M> itemBuilder)
|
||||
throws IOException, InterruptedException, ResponseTooLargeException {
|
||||
clearOnReload();
|
||||
|
||||
String cacheKey = itemBuilder.makeCacheKey();
|
||||
|
||||
try {
|
||||
@ -221,6 +226,12 @@ public class LRUCache<M> {
|
||||
removeCacheEntriesByPredicate(e -> true);
|
||||
}
|
||||
|
||||
private void clearOnReload() {
|
||||
if (reloadDetector.hasReloadOccurred()) {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove all cache entries (and their cache files) that match the predicate. */
|
||||
private void removeCacheEntriesByPredicate(Predicate<CacheEntry<M>> predicate) {
|
||||
List<Map.Entry<String, CacheEntry<M>>> toRemove =
|
||||
@ -352,6 +363,11 @@ public class LRUCache<M> {
|
||||
return settings;
|
||||
}
|
||||
|
||||
/** Public for testing. */
|
||||
public void simulateReloadTestOnly() {
|
||||
reloadDetector.simulateReloadTestOnly();
|
||||
}
|
||||
|
||||
private record CacheEntry<M>(File responseData, M metadata, long size, ZonedDateTime expiry) {}
|
||||
|
||||
/**
|
||||
|
39
std-bits/base/src/main/java/org/enso/base/cache/ReloadDetector.java
vendored
Normal file
39
std-bits/base/src/main/java/org/enso/base/cache/ReloadDetector.java
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package org.enso.base.cache;
|
||||
|
||||
import org.enso.base.polyglot.EnsoMeta;
|
||||
import org.graalvm.polyglot.Value;
|
||||
|
||||
/**
|
||||
* Detects that the reload button has been pressed.
|
||||
*
|
||||
* <p>.hasReloadOccurred() returns true if the reload button was pressed since the last call to
|
||||
* .hasReloadOccurred().
|
||||
*
|
||||
* <p>This uses a `Managed_Resource` (created in eval'd Enso code) that is cleared on reload.
|
||||
*/
|
||||
public class ReloadDetector {
|
||||
private Value ensoReloadDetector;
|
||||
|
||||
public ReloadDetector() {
|
||||
resetEnsoReloadDetector();
|
||||
}
|
||||
|
||||
public boolean hasReloadOccurred() {
|
||||
var reloadHasOccurred = ensoReloadDetector.invokeMember("has_reload_occurred").asBoolean();
|
||||
if (reloadHasOccurred) {
|
||||
resetEnsoReloadDetector();
|
||||
}
|
||||
return reloadHasOccurred;
|
||||
}
|
||||
|
||||
private void resetEnsoReloadDetector() {
|
||||
ensoReloadDetector =
|
||||
EnsoMeta.callStaticModuleMethod(
|
||||
"Standard.Base.Network.Reload_Detector", "create_reload_detector");
|
||||
}
|
||||
|
||||
void simulateReloadTestOnly() {
|
||||
EnsoMeta.callStaticModuleMethod(
|
||||
"Standard.Base.Network.Reload_Detector", "simulate_reload_test_only", ensoReloadDetector);
|
||||
}
|
||||
}
|
@ -19,12 +19,10 @@ import org.enso.base.cache.ResponseTooLargeException;
|
||||
import org.enso.base.net.URISchematic;
|
||||
import org.enso.base.net.URIWithSecrets;
|
||||
import org.graalvm.collections.Pair;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Value;
|
||||
|
||||
/** Makes HTTP requests with secrets in either header or query string. */
|
||||
public final class EnsoSecretHelper extends SecretValueResolver {
|
||||
private static Value cache;
|
||||
private static EnsoHTTPResponseCache cache;
|
||||
|
||||
/** Gets a JDBC connection resolving EnsoKeyValuePair into the properties. */
|
||||
public static Connection getJDBCConnection(
|
||||
@ -179,43 +177,10 @@ public final class EnsoSecretHelper extends SecretValueResolver {
|
||||
}
|
||||
|
||||
public static EnsoHTTPResponseCache getOrCreateCache() {
|
||||
if (getCache() instanceof EnsoHTTPResponseCache httpCache) {
|
||||
return httpCache;
|
||||
} else {
|
||||
var module =
|
||||
Context.getCurrent()
|
||||
.eval(
|
||||
"enso",
|
||||
"""
|
||||
import Standard.Base.Runtime.Managed_Resource.Managed_Resource
|
||||
import Standard.Base.Data.Boolean.Boolean
|
||||
|
||||
type Cache
|
||||
private Value ref:Managed_Resource
|
||||
|
||||
new obj -> Cache =
|
||||
on_finalize _ = 0
|
||||
ref = Managed_Resource.register obj on_finalize Boolean.True
|
||||
Cache.Value ref
|
||||
|
||||
get self = self.ref.with (r->r)
|
||||
""");
|
||||
var cacheNew = module.invokeMember("eval_expression", "Cache.new");
|
||||
var httpCache = new EnsoHTTPResponseCache();
|
||||
cache = cacheNew.execute(httpCache);
|
||||
return httpCache;
|
||||
}
|
||||
}
|
||||
|
||||
public static EnsoHTTPResponseCache getCache() {
|
||||
var c = cache instanceof Value v ? v.invokeMember("get") : null;
|
||||
if (c != null
|
||||
&& c.isHostObject()
|
||||
&& c.asHostObject() instanceof EnsoHTTPResponseCache httpCache) {
|
||||
return httpCache;
|
||||
} else {
|
||||
return null;
|
||||
if (cache == null) {
|
||||
cache = new EnsoHTTPResponseCache();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static final Comparator<Pair<String, String>> headerNameComparator =
|
||||
|
@ -95,6 +95,7 @@ add_specs suite_builder = suite_builder.group "Managed_Resource" group_builder->
|
||||
|
||||
# finalizes the resource
|
||||
mr.finalize
|
||||
mr.has_been_finalized . should_be_true
|
||||
builder.append "Finalized:"+mr.to_text
|
||||
|
||||
# operation on finalized resource
|
||||
|
@ -172,6 +172,9 @@ add_specs suite_builder =
|
||||
lru_cache = LRUCache.new
|
||||
with_lru_cache lru_cache action
|
||||
|
||||
fake_reload =
|
||||
EnsoSecretHelper.getOrCreateCache.getLRUCache.simulateReloadTestOnly
|
||||
|
||||
url0 = base_url_with_slash+'test_download?max-age=16&length=10'
|
||||
url1 = base_url_with_slash+'test_download?max-age=16&length=20'
|
||||
url_post = base_url_with_slash + "post"
|
||||
@ -541,6 +544,27 @@ add_specs suite_builder =
|
||||
Test_Environment.unsafe_with_environment_override "ENSO_LIB_HTTP_CACHE_MAX_TOTAL_CACHE_LIMIT" "101%" <|
|
||||
LRUCache.new . getSettings . getTotalCacheLimit . should_equal (TotalCacheLimit.Percentage.new 0.2)
|
||||
|
||||
group_builder.specify "Cache should be cleared when a reload is detected" <|
|
||||
HTTP.fetch base_url_with_slash+'test_download?length=10'
|
||||
HTTP.fetch base_url_with_slash+'test_download?length=11'
|
||||
HTTP.fetch base_url_with_slash+'test_download?length=12'
|
||||
get_num_response_cache_entries . should_equal 3
|
||||
|
||||
fake_reload
|
||||
|
||||
get_num_response_cache_entries . should_equal 3 # Cleaning is not triggered until the next request
|
||||
HTTP.fetch base_url_with_slash+'test_download?length=10'
|
||||
get_num_response_cache_entries . should_equal 1
|
||||
HTTP.fetch base_url_with_slash+'test_download?length=14'
|
||||
HTTP.fetch base_url_with_slash+'test_download?length=15'
|
||||
get_num_response_cache_entries . should_equal 3
|
||||
|
||||
fake_reload
|
||||
|
||||
get_num_response_cache_entries . should_equal 3 # Cleaning is not triggered until the next request
|
||||
HTTP.fetch base_url_with_slash+'test_download?length=16'
|
||||
get_num_response_cache_entries . should_equal 1
|
||||
|
||||
group_builder.specify "Reissues the request if the cache file disappears" pending=pending_has_url <| Test.with_retries <|
|
||||
with_default_cache <|
|
||||
url = base_url_with_slash+'test_download?max-age=16&length=10'
|
||||
|
Loading…
Reference in New Issue
Block a user