Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear cache on reload #11673

Merged
merged 68 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5266a4b
Using Ref.new allow_gc=True to implement in-memory caches
JaroslavTulach Nov 18, 2024
d803b28
Hide operations with References behind @TruffleBoundary
JaroslavTulach Nov 18, 2024
38a20b4
Work with v and not this.value
JaroslavTulach Nov 18, 2024
0f437fc
Merging with latest develop
JaroslavTulach Nov 20, 2024
404e3cc
Fixing typo
JaroslavTulach Nov 20, 2024
5629e20
Using Ref.new to cache reference to EnsoHTTPResponseCache
JaroslavTulach Nov 20, 2024
b1d4b37
Removing (unused) support for Name.Special
JaroslavTulach Nov 20, 2024
0e42e9d
Fixing moved import
JaroslavTulach Nov 20, 2024
0b4e1f8
Providing access to bodyNode in the builtin methods
JaroslavTulach Nov 20, 2024
f5f1614
Enabling allow_gc in the caches
JaroslavTulach Nov 20, 2024
dad2bb6
Note in changelog
JaroslavTulach Nov 20, 2024
ada146e
Merge branch 'wip/jtulach/ReferenceManager11485' into wip/gmt/11485-c…
GregoryTravis Nov 25, 2024
e26a868
wip
GregoryTravis Nov 25, 2024
da895d8
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Nov 26, 2024
96ea1f2
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Nov 26, 2024
e003b8a
revert ESH changes
GregoryTravis Nov 26, 2024
5d97a2a
1 test
GregoryTravis Nov 26, 2024
6a1e89f
docs
GregoryTravis Nov 26, 2024
c493ec0
Clear references on reload
GregoryTravis Nov 26, 2024
131c52e
doc
GregoryTravis Nov 27, 2024
4f750b6
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Nov 27, 2024
5b563d4
Can no longer invoke Managed_Resource.with when Managed_Resource.fina…
JaroslavTulach Nov 27, 2024
dc2decd
remove reference release builtin and simulate reload
GregoryTravis Nov 27, 2024
1fb2517
Merge branch 'wip/gmt/11485-clear-cache' of github.com:enso-org/enso …
GregoryTravis Nov 27, 2024
ef15d90
Can no longer invoke Managed_Resource.with when Managed_Resource.fina…
JaroslavTulach Nov 27, 2024
0738190
Merge branch 'wip/gmt/11485-clear-cache' of github.com:enso-org/enso …
GregoryTravis Nov 27, 2024
5f176e7
fmt
GregoryTravis Nov 27, 2024
85e8b6d
Can no longer invoke Managed_Resource.with when Managed_Resource.fina…
JaroslavTulach Nov 27, 2024
2e9d0c3
Merge branch 'wip/jtulach/ReferenceManager11485' into wip/gmt/11485-c…
GregoryTravis Nov 27, 2024
46ef5fb
revert
GregoryTravis Nov 27, 2024
e56b867
Backing out the Ref changes
JaroslavTulach Nov 28, 2024
3ca29aa
Merge tag '2024.5.1-nightly.2024.11.27' into wip/jtulach/ReferenceMan…
JaroslavTulach Nov 28, 2024
da2d438
Ref.new takes only one argument (again)
JaroslavTulach Nov 28, 2024
ae83008
Commenting out releaseAll call for now
JaroslavTulach Nov 28, 2024
276a885
Allow system controlled Managed_Resource
JaroslavTulach Nov 28, 2024
205811c
Merging with most recent develop
JaroslavTulach Nov 28, 2024
40aedfc
on_missing behavior for managed resources that get access after being…
JaroslavTulach Nov 28, 2024
2e91135
Updating micro-distribution with the new Managed_Resource builtins
JaroslavTulach Nov 28, 2024
786f156
Moving the GC related parts of RefTest to ManagedResourceTest
JaroslavTulach Nov 28, 2024
934ec5f
Better note in changelog
JaroslavTulach Nov 28, 2024
a3c07e0
Making public so the Fetch_Spec passes OK
JaroslavTulach Nov 28, 2024
a7890d2
Using Managed_Resource inside of EnsoSecretHelper
JaroslavTulach Nov 28, 2024
7c3c33c
merge mr-only
GregoryTravis Nov 29, 2024
20b14b5
update to mr-only
GregoryTravis Nov 29, 2024
375fb31
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Nov 29, 2024
c9ee885
schedule finalization
GregoryTravis Nov 29, 2024
00ccaca
green, clear returns Nothing
GregoryTravis Dec 1, 2024
2899524
merge
GregoryTravis Dec 3, 2024
eeba1dc
wip
GregoryTravis Dec 3, 2024
afa0dfc
remove Jaroslav POC
GregoryTravis Dec 3, 2024
4f22fe5
move ReloadDetector inline enso to a module
GregoryTravis Dec 3, 2024
0aa2490
wip
GregoryTravis Dec 3, 2024
2b01606
changelog
GregoryTravis Dec 3, 2024
a041045
fmt
GregoryTravis Dec 3, 2024
9b34a56
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Dec 4, 2024
1f13014
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Dec 4, 2024
8ca2e23
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Dec 5, 2024
eebe998
wip
GregoryTravis Dec 5, 2024
f168299
fake with finalize
GregoryTravis Dec 5, 2024
95208ac
wip
GregoryTravis Dec 5, 2024
9796234
wip
GregoryTravis Dec 5, 2024
07f413d
wip
GregoryTravis Dec 5, 2024
e6ad441
wip
GregoryTravis Dec 5, 2024
c2c1ada
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Dec 9, 2024
828ac2d
review
GregoryTravis Dec 9, 2024
be50dcd
fmt
GregoryTravis Dec 10, 2024
d036b8b
Merge branch 'develop' into wip/gmt/11485-clear-cache
GregoryTravis Dec 10, 2024
5aa4003
repeat url in fake reload test
GregoryTravis Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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
GregoryTravis marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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
GregoryTravis marked this conversation as resolved.
Show resolved Hide resolved
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"
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -42,6 +43,10 @@ class RecomputeContextCmd(
ec: ExecutionContext
): Future[Boolean] = {
Future {
EnsoContext
.get(null)
.getResourceManager()
.scheduleFinalizationOfSystemReferences();
ctx.jobControlPlane.abortJobs(
request.contextId,
"recompute context",
Expand Down
16 changes: 16 additions & 0 deletions std-bits/base/src/main/java/org/enso/base/cache/LRUCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand All @@ -89,6 +92,8 @@ public LRUCache(LRUCacheSettings settings, NowGetter nowGetter, DiskSpaceGetter
*/
public CacheResult<M> getResult(ItemBuilder<M> itemBuilder)
throws IOException, InterruptedException, ResponseTooLargeException {
clearOnReload();

String cacheKey = itemBuilder.makeCacheKey();

try {
Expand Down Expand Up @@ -221,6 +226,12 @@ public void clear() {
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 =
Expand Down Expand Up @@ -344,6 +355,11 @@ public LRUCacheSettings getSettings() {
return settings;
}

/** Public for testing. */
public void simulateReloadTestOnly() {
reloadDetector.simulateReloadTestOnly();
}
Comment on lines +358 to +361
Copy link
Member

@radeusgd radeusgd Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced about this mock.

After all - tests relying on this mock use completely different logic for checking if the cache should be cleared than what will be running in the IDE. The Reload_Detector is a new component and it is not tested anywhere at all (apart from manual testing).

I think it would be best to add a test to the JVM tests, similar to something like RuntimeExecutionEnvironmentTest that checks the refresh logic is correctly connected through the Language Server. Perhaps we don't need to test it all the way down to HTTP. It would probably suffice to have a test that instantiates the Java ReloadDetector and checks that it indeed detects cache invalidation correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think perhaps we may want to proceed with this PR, as it's needed for release, with just manual testing. But I think it would be good to have some test that ensures that this works as expected.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a test to the JVM tests, similar to something like RuntimeExecutionEnvironmentTest that checks the refresh logic is correctly connected through the Language Server.

Logic of Managed_Resource.register ref on_finalize Boolean.True is unit tested at

ensoCtx.getResourceManager().scheduleFinalizationOfSystemReferences();

If we have a unit test of Managed_Resource and another unit test for ReloadDetector, then I believe we have 100% coverage!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point was that we are not testing the integration between ReloadDetector and Managed_Resource logics if we rely on a mock when testing ReloadDetector.

The problem is clear in the get bug which if I understand correctly you agree with me is a bug - we are not catching the possible Uninitialized_State there, possibly returning dataflow error instead of boolean. This is not covered by any existing test as far as I can tell. Given that we seem to have a bug and no test seems to be catching the bug - I'd say we don't have good enough tests for this facility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that a test like this is necessary -- precisely because I have already had the tests pass but the feature fail to work. Thus: #11792.


private record CacheEntry<M>(File responseData, M metadata, long size, ZonedDateTime expiry) {}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@
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(
Expand Down Expand Up @@ -179,43 +177,10 @@ public EnsoHttpResponse reconstructResponseFromCachedStream(
}

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 =
Expand Down
1 change: 1 addition & 0 deletions test/Base_Tests/src/Runtime/Managed_Resource_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions test/Table_Tests/src/IO/Fetch_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -517,6 +520,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'
Expand Down
Loading