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

chore(FTM) Adding cache to the Queries Refs#31246 #31293

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,17 @@ protected Contentlet findContentletByIdentifier(final String identifier, final l
throws DotDataException {

final String variant = UtilMethods.isSet(variantId) ? variantId : DEFAULT_VARIANT.name();

final Optional<Contentlet> timeMachine = contentletCache.getTimeMachine(timeMachineDate, identifier, variant);
if(timeMachine.isPresent()){
final Contentlet contentlet = timeMachine.get();
if(!CACHE_404_CONTENTLET.equals(contentlet.getInode())){
return contentlet;
} else {
return null;
}
}

// Perhaps we should exclude here contents with only a working version and no live version whatsoever
// To match what we did in the old time machine days
// This logic is tied with a fragment of VTL code in ContainerLoader.java check it out
Expand Down Expand Up @@ -1258,8 +1269,14 @@ protected Contentlet findContentletByIdentifier(final String identifier, final l
.addParam(identifier)
.addParam(variant);

List<Contentlet> con = TransformerLocator.createContentletTransformer(dotConnect.loadObjectResults()).asList();
return con.isEmpty() ? null : con.get(0);
final List<Contentlet> con = TransformerLocator.createContentletTransformer(dotConnect.loadObjectResults()).asList();
if (!con.isEmpty()){
final Contentlet contentlet = con.get(0);
contentletCache.addTimeMachine(timeMachineDate, identifier, contentlet);
return contentlet;
}
contentletCache.addTimeMachine(timeMachineDate, identifier, cache404Content);
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6177,6 +6177,8 @@ private void updatePublishAndExpireDates(final Contentlet contentlet, final User
}
}
}
//if any change on the publish, expire dates occurs, we better kick it out from TM cache
CacheLocator.getContentletCache().invalidateTimeMachine(contentlet);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public void run() {
Contentlet contentlet =
contentletAPI.findContentletByIdentifierAnyLanguage(contentletId, variantName);

if (contentlet == null && !VariantAPI.DEFAULT_VARIANT.equals(variantName)) {
if (contentlet == null && !VariantAPI.DEFAULT_VARIANT.name().equals(variantName)) {
contentlet = contentletAPI.findContentletByIdentifierAnyLanguage(contentletId,
VariantAPI.DEFAULT_VARIANT.name());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.dotcms.content.elasticsearch.business.ESContentFactoryImpl.TranslatedQuery;
import com.dotmarketing.business.Cachable;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import java.util.Date;
import java.util.Optional;

//This interface should have default package access
public abstract class ContentletCache implements Cachable {
Expand All @@ -20,4 +22,12 @@ public abstract class ContentletCache implements Cachable {
public abstract TranslatedQuery getTranslatedQuery(String key);

public abstract void remove(Contentlet contentlet);

public abstract void addTimeMachine(Date timeMachineDate, String identifier, Contentlet content);

public abstract Optional<Contentlet> getTimeMachine(Date timeMachineDate, String identifier, String variant);

public abstract void invalidateTimeMachine(Contentlet content);

public abstract void invalidateTimeMachine();
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,35 @@
import com.dotmarketing.db.DbConnectionFactory;
import com.dotmarketing.portlets.contentlet.model.Contentlet;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.StringUtils;
import com.liferay.util.StringPool;
import io.vavr.control.Try;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
* @author Jason Tesser
* @since 1.6
*/
public class ContentletCacheImpl extends ContentletCache {

private DotCacheAdministrator cache;
private final DotCacheAdministrator cache;

private String primaryGroup = "ContentletCache";
private static final String primaryGroup = "ContentletCache";

private String translatedQueryGroup = "TranslatedQueryCache";
private static final String translatedQueryGroup = "TranslatedQueryCache";
// region's name for the cache
private String[] groupNames = {primaryGroup, HostCache.PRIMARY_GROUP, translatedQueryGroup};

private static final String timeMachineByIdentifierGroup = "TimeMachineByIdentifierCache";

private static final String[] groupNames = {
primaryGroup,
HostCache.PRIMARY_GROUP,
translatedQueryGroup,
timeMachineByIdentifierGroup
};

public ContentletCacheImpl() {
cache = CacheLocator.getCacheAdministrator();
Expand Down Expand Up @@ -108,6 +123,8 @@ public void remove(final com.dotmarketing.portlets.contentlet.model.Contentlet c

//Invalidating relationship cache
CacheLocator.getRelationshipCache().removeRelatedContentMap(contentlet.getIdentifier());

invalidateTimeMachine(contentlet);
}
/* (non-Javadoc)
* @see com.dotmarketing.business.PermissionCache#remove(java.lang.String)
Expand Down Expand Up @@ -140,8 +157,10 @@ public void remove(final String key){
CacheLocator.getHostCache().remove(host);
}

if (content != null && content.getIdentifier() != null)
if (content != null && content.getIdentifier() != null){
CacheLocator.getHTMLPageCache().remove(content.getIdentifier());
invalidateTimeMachine(content);
}

new ShortyIdCache().remove(key);

Expand All @@ -160,4 +179,79 @@ public String getPrimaryGroup() {
public Contentlet add(Contentlet content) {
return add(content.getInode(), content);
}


/**
* Retrieves the cached time machine map for a given identifier.
* If the cache does not exist, it initializes a new one.
* @param identifier The unique identifier of the content.
* @return A map associating timestamped keys with {@link Contentlet} instances.
*/
private Map<String, Contentlet> getTimeMachineByIdentifierCache(final String identifier) {
@SuppressWarnings("unchecked")
Map<String, Map<String, Contentlet>> byIdentifierCache =
(Map<String, Map<String, Contentlet>>) Try.of(()->cache.get(identifier,
timeMachineByIdentifierGroup)).getOrElse(Map.of());

if (byIdentifierCache == null) {
byIdentifierCache = new ConcurrentHashMap<>();
cache.put(identifier, byIdentifierCache, timeMachineByIdentifierGroup);
}

return byIdentifierCache.computeIfAbsent(
identifier, k -> new ConcurrentHashMap<>());
}


/**
* Stores a {@link Contentlet} in the time machine cache, associating it with a specific
* identifier and timestamp.
* @param timeMachineDate The timestamp representing the moment the content was stored.
* @param identifier The unique identifier of the content.
* @param content The {@link Contentlet} instance to be cached.
*/
@Override
public void addTimeMachine(final Date timeMachineDate, final String identifier, final Contentlet content) {
final String key = content.getVariantId() + StringPool.COLON + timeMachineDate.getTime();
getTimeMachineByIdentifierCache(identifier).put(key, content);
}

/**
* Retrieves a {@link Contentlet} from the time machine cache based on the identifier,
* variant, and timestamp. If the operation is executed within a transaction,
* the method returns an empty result to avoid inconsistencies.
* @param timeMachineDate The timestamp associated with the content.
* @param identifier The unique identifier of the content.
* @param variant The variant identifier of the content.
* @return An {@link Optional} containing the retrieved {@link Contentlet}, or empty if not found.
*/
@Override
public Optional<Contentlet> getTimeMachine(final Date timeMachineDate, final String identifier, final String variant) {
if(DbConnectionFactory.inTransaction()) {
Logger.debug(this, "In transaction, returning empty");
return Optional.empty();
}
final String key = variant + StringPool.COLON + timeMachineDate.getTime();
return Optional.ofNullable(getTimeMachineByIdentifierCache(identifier).get(key));
}

/**
* Invalidates and removes all cached time machine data related to a given {@link Contentlet}.
* @param content The {@link Contentlet} instance whose cache should be invalidated.
*/
@Override
public void invalidateTimeMachine(final Contentlet content) {
if(null != content && StringUtils.isSet(content.getIdentifier())) {
cache.remove(content.getIdentifier(), timeMachineByIdentifierGroup);
}
}

/**
* Invalidates and removes all cached time machine data.
*/
@Override
public void invalidateTimeMachine(){
cache.flushGroup(timeMachineByIdentifierGroup);
}

}
Loading