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

fix: resource reloading #673

Merged
merged 8 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
3 changes: 1 addition & 2 deletions ee/src/main/java/io/supertokens/ee/EEFeatureFlag.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import com.google.gson.*;
import io.supertokens.Main;
import io.supertokens.ProcessState;
import io.supertokens.cronjobs.Cronjobs;
import io.supertokens.cronjobs.telemetry.Telemetry;
import io.supertokens.ee.cronjobs.EELicenseCheck;
import io.supertokens.featureflag.EE_FEATURES;
Expand Down Expand Up @@ -94,7 +93,7 @@ public void updateEnabledFeaturesValueReadFromDbTime(long newTime) {
public void constructor(Main main, AppIdentifier appIdentifier) {
this.main = main;
this.appIdentifier = appIdentifier;
Cronjobs.addCronjob(main, EELicenseCheck.getNewInstance(main, this.appIdentifier.getAsPublicTenantIdentifier()));
EELicenseCheck.getOrCreateInstance(main);
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
try {
this.syncFeatureFlagWithLicenseKey();
} catch (HttpResponseException | IOException e) {
Expand Down
36 changes: 18 additions & 18 deletions ee/src/main/java/io/supertokens/ee/cronjobs/EELicenseCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,36 @@
import io.supertokens.cronjobs.Cronjobs;
import io.supertokens.ee.EEFeatureFlag;
import io.supertokens.featureflag.FeatureFlag;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.storageLayer.StorageLayer;

import java.util.List;

public class EELicenseCheck extends CronTask {

public static final String RESOURCE_KEY = "io.supertokens.ee.cronjobs.EELicenseCheck";

private EELicenseCheck(Main main, TenantIdentifier targetTenant) {
super("EELicenseCheck", main, targetTenant);
}
private static EELicenseCheck instance = null;

public static EELicenseCheck getNewInstance(Main main, TenantIdentifier tenantIdentifier) {
try {
EELicenseCheck existingCronTask = (EELicenseCheck) main.getResourceDistributor()
.getResource(tenantIdentifier, RESOURCE_KEY);
private EELicenseCheck(Main main, List<List<TenantIdentifier>> tenantsInfo) {
super("EELicenseCheck", main, tenantsInfo, true);
}
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved

Cronjobs.removeCronjob(main, existingCronTask);
main.getResourceDistributor().removeResource(tenantIdentifier, RESOURCE_KEY);
} catch (TenantOrAppNotFoundException e) {
// ignore
public static synchronized EELicenseCheck getOrCreateInstance(Main main) {
if (instance != null) {
return instance;
}
return (EELicenseCheck) main.getResourceDistributor()
.setResource(tenantIdentifier, RESOURCE_KEY,
new EELicenseCheck(main, tenantIdentifier));

instance = new EELicenseCheck(main, StorageLayer.getTenantsWithUniqueUserPoolId(main));
Cronjobs.addCronjob(main, instance);

return instance;
}

@Override
protected void doTaskForTargetTenant(TenantIdentifier tenantIdentifier) throws Exception {
// this cronjob is for one tenant only (the targetTenant provided in the constructor)
FeatureFlag.getInstance(main, tenantIdentifier.toAppIdentifier()).syncFeatureFlagWithLicenseKey();
protected void doTaskPerApp(AppIdentifier app) throws Exception {
FeatureFlag.getInstance(main, app).syncFeatureFlagWithLicenseKey();
}

@Override
Expand Down
22 changes: 14 additions & 8 deletions src/main/java/io/supertokens/multitenancy/MultitenancyHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,20 @@ public List<TenantIdentifier> refreshTenantsInCoreIfRequired(boolean reloadAllRe
try {
TenantConfig[] tenantsFromDb = getAllTenantsFromDb();

rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
Map<ResourceDistributor.KeyClass, JsonObject> normalizedTenantsFromDb = Config.getNormalisedConfigsForAllTenants(
tenantsFromDb, Config.getBaseConfigAsJsonObject(main));

Map<ResourceDistributor.KeyClass, JsonObject> normalizedTenantsFromMemory = Config.getNormalisedConfigsForAllTenants(
this.tenantConfigs, Config.getBaseConfigAsJsonObject(main));

List<TenantIdentifier> tenantsThatChanged = new ArrayList<>();

Map<TenantIdentifier, TenantConfig> fromDb = new HashMap<>();
for (TenantConfig t : tenantsFromDb) {
fromDb.put(t.tenantIdentifier, t);
}
for (TenantConfig t : this.tenantConfigs) {
TenantConfig fromDbConfig = fromDb.get(t.tenantIdentifier);
if (!t.deepEquals(fromDbConfig)) {
tenantsThatChanged.add(t.tenantIdentifier);
for (Map.Entry<ResourceDistributor.KeyClass, JsonObject> entry : normalizedTenantsFromMemory.entrySet()) {
JsonObject tenantConfigFromMemory = entry.getValue();
JsonObject tenantConfigFromDb = normalizedTenantsFromDb.get(entry.getKey());

if (!tenantConfigFromMemory.equals(tenantConfigFromDb)) {
tenantsThatChanged.add(entry.getKey().getTenantIdentifier());
}
}

Expand Down Expand Up @@ -196,6 +200,8 @@ private void refreshCronjobs() {
public TenantConfig[] getAllTenants() {
try {
return main.getResourceDistributor().withResourceDistributorLockWithReturn(() -> {
// Returning a deep copy of the tenantConfigs array so that the functions consuming it
// do not modify the original array
TenantConfig[] tenantConfigs = new TenantConfig[this.tenantConfigs.length];
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved

for (int i = 0; i < this.tenantConfigs.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public static void loadAllTenantStorage(Main main, TenantConfig[] tenants, List<
String userPoolId = currStorage.getUserPoolId();
String connectionPoolId = currStorage.getConnectionPoolId();
String uniqueId = userPoolId + "~" + connectionPoolId;
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
if (idToExistingStorageLayerMap.containsKey(uniqueId)) {
rishabhpoddar marked this conversation as resolved.
Show resolved Hide resolved
if (idToExistingStorageLayerMap.containsKey(uniqueId) && !tenantsThatChanged.contains(key.getTenantIdentifier())) {
// we reuse the existing storage layer
resourceKeyToStorageMap.put(key, idToExistingStorageLayerMap.get(uniqueId).storage);
}
Expand Down
77 changes: 76 additions & 1 deletion src/test/java/io/supertokens/test/multitenant/ConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,81 @@ public void testThatConfigChangesReloadsConfig() throws Exception {
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

@Test
public void testThatConfigChangesInAppReloadsConfigInTenant() throws Exception {
String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY});
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));

TenantIdentifier a1 = new TenantIdentifier(null, "a1", null);
TenantIdentifier t1 = new TenantIdentifier(null, "a1", "t1");

{
JsonObject coreConfig = new JsonObject();
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
a1,
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
coreConfig
), false);
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
t1,
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
coreConfig
), false);
}

{
Config configBefore = Config.getInstance(t1, process.getProcess());

JsonObject coreConfig = new JsonObject();
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
a1,
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
coreConfig
), false);Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
t1,
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
coreConfig
), false);

Config configAfter = Config.getInstance(t1, process.getProcess());

assertEquals(configBefore, configAfter);
}

{
Config configBefore = Config.getInstance(t1, process.getProcess());

JsonObject coreConfig = new JsonObject();
coreConfig.addProperty("email_verification_token_lifetime", 2000);
Multitenancy.addNewOrUpdateAppOrTenant(process.getProcess(), new TenantConfig(
a1,
new EmailPasswordConfig(true),
new ThirdPartyConfig(true, null),
new PasswordlessConfig(true),
coreConfig
), false);

Config configAfter = Config.getInstance(t1, process.getProcess());

assertNotEquals(configBefore, configAfter);
}

process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
}

@Test
public void testThatConfigChangesReloadsStorageLayer() throws Exception {
String[] args = {"../"};
Expand Down Expand Up @@ -1343,7 +1418,7 @@ public void testThatConfigChangesReloadsStorageLayer() throws Exception {

Storage storageLayerAfter = StorageLayer.getStorage(t1, process.getProcess());

assertEquals(storageLayerBefore, storageLayerAfter);
assertNotEquals(storageLayerBefore, storageLayerAfter);
}

{
Expand Down