Add a cache around CloudConfigProvider

This commit is contained in:
Kris De Volder
2017-07-19 12:35:43 -07:00
parent 86462216f7
commit d15dae1c0c
10 changed files with 116 additions and 12 deletions

View File

@@ -14,8 +14,9 @@ import java.util.Collection;
import java.util.Map;
import java.util.UUID;
import org.springframework.ide.vscode.bosh.cloudconfig.CloudConfigModel;
import org.springframework.ide.vscode.bosh.cloudconfig.DynamicModelProvider;
import org.springframework.ide.vscode.bosh.models.CachingModelProvider;
import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
@@ -83,7 +84,7 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
public BoshDeploymentManifestSchema(ASTTypeCache astTypes, DynamicModelProvider<CloudConfigModel> cloudConfigProvider) {
this.astTypes = astTypes;
this.cloudConfigProvider = cloudConfigProvider;
this.cloudConfigProvider = new CachingModelProvider<>(cloudConfigProvider);
TYPE_UTIL = f.TYPE_UTIL;
V2_TOPLEVEL_TYPE = createV2Schema();

View File

@@ -10,8 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.bosh;
import org.springframework.ide.vscode.bosh.cloudconfig.CloudConfigModel;
import org.springframework.ide.vscode.bosh.cloudconfig.DynamicModelProvider;
import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
import org.springframework.ide.vscode.bosh.models.DynamicModelProvider;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter.LazyCompletionResolver;
import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider;

View File

@@ -12,7 +12,7 @@ package org.springframework.ide.vscode.bosh;
import java.io.IOException;
import org.springframework.ide.vscode.bosh.cloudconfig.BoshCommandCloudConfigProvider;
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
public class Main {

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.bosh.cloudconfig;
package org.springframework.ide.vscode.bosh.models;
import java.io.File;
import java.time.Duration;

View File

@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.bosh.models;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/**
* Wraps around a {@link DynamicModelProvider} to add a cache.
*/
public class CachingModelProvider<T> implements DynamicModelProvider<T> {
/**
* Special key to use when the actual key is null (because guava cache doesn't
* like null keys).
*/
private static final Object NULL_KEY = new Object();
private long timeout = 15;
private TimeUnit timeoutUnit = TimeUnit.SECONDS;
private Cache<Object, T> cache = createCache();
private final DynamicModelProvider<T> delegate;
public CachingModelProvider(DynamicModelProvider<T> delegate) {
this.delegate = delegate;
}
/**
* Function used to determine the caching key, given the current {@link DynamicSchemaContext}.
* <p>
* The default keyGetter ignores the context and just returns the same object all the time. This
* results in a cache that only keeps a single value (since there's only a single key ever used
* to store / find cache entries.
*/
private Function<DynamicSchemaContext, Object> keyGetter = (dc) -> "WHATEVER";
protected Cache<Object, T> createCache() {
return CacheBuilder.newBuilder()
.expireAfterWrite(timeout, timeoutUnit)
.build();
}
public CachingModelProvider<T> setTimeout(long timeout, TimeUnit unit) {
this.timeout = timeout;
this.timeoutUnit = unit;
return this;
}
@Override
public T getModel(DynamicSchemaContext dc) throws Exception {
Object key = keyGetter.apply(dc);
if (key==null) {
//guava cache doesn't like null key
key = NULL_KEY;
}
return cache.get(key, () -> delegate.getModel(dc));
}
}

View File

@@ -8,7 +8,7 @@
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.bosh.cloudconfig;
package org.springframework.ide.vscode.bosh.models;
import java.util.Collection;

View File

@@ -1,4 +1,4 @@
package org.springframework.ide.vscode.bosh.cloudconfig;
package org.springframework.ide.vscode.bosh.models;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;

View File

@@ -14,9 +14,9 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.ide.vscode.bosh.cloudconfig.BoshCommandCloudConfigProvider;
import org.springframework.ide.vscode.bosh.cloudconfig.CloudConfigModel;
import org.springframework.ide.vscode.bosh.mocks.MockCloudConfigProvider;
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
import org.springframework.ide.vscode.bosh.models.CloudConfigModel;
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
import com.google.common.collect.ImmutableMultiset;

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.bosh;
import static org.junit.Assert.assertEquals;
import static org.springframework.ide.vscode.languageserver.testharness.Editor.PLAIN_COMPLETION;
import java.io.IOException;
@@ -849,5 +850,27 @@ public class BoshEditorTest {
"default<*>",
"large<*>"
);
//Verify that the cache is working. Shouldn't read the cc provier more than once, evem for multiple CA requests.
editor.assertCompletionLabels("default", "large");
editor.assertCompletionLabels("default", "large");
assertEquals(1, cloudConfigProvider.getReadCount());
}
@Test public void reconcileVMtype() throws Exception {
Editor editor = harness.newEditor(
"name: foo\n" +
"instance_groups: \n" +
"- name: some-server\n" +
" vm_type: bogus-vm\n" +
"- name: other-server\n" +
" vm_type: large"
);
editor.ignoreProblem(YamlSchemaProblems.MISSING_PROPERTY);
editor.assertProblems(
"bogus-vm|unknown 'VMType'. Valid values are: [default, large]"
);
assertEquals(1, cloudConfigProvider.getReadCount());
}
}

View File

@@ -13,7 +13,7 @@ package org.springframework.ide.vscode.bosh.mocks;
import java.util.concurrent.Callable;
import org.springframework.ide.vscode.bosh.BoshCommandCloudConfigProviderTest;
import org.springframework.ide.vscode.bosh.cloudconfig.BoshCommandCloudConfigProvider;
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
import org.springframework.ide.vscode.commons.util.IOUtil;
public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvider {
@@ -22,12 +22,15 @@ public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvide
private Callable<String> cloudConfigReader = () -> IOUtil.toString(BoshCommandCloudConfigProviderTest.class.getResourceAsStream(MOCK_DATA_RSRC));
private int readCount = 0;
/**
* Override with a 'fake' which just returns some mock data. That way we can unit-test
* without requiring a real bosh setup.
*/
@Override
protected String executeBoshCloudConfigCommand() throws Exception {
readCount++;
return cloudConfigReader.call();
}
@@ -35,4 +38,8 @@ public final class MockCloudConfigProvider extends BoshCommandCloudConfigProvide
this.cloudConfigReader = reader;
return this;
}
public int getReadCount() {
return readCount;
}
}