Skip to content

Commit

Permalink
[ALFREDOPS-864] bug fix for integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pvriel committed Jan 8, 2025
1 parent 65d7d26 commit 3c5c67b
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 185 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
*
* @since 0.5.0
*/
public abstract class ToggleableHealthFixerPlugin implements HealthFixerPlugin {
public interface ToggleableHealthFixerPlugin extends HealthFixerPlugin {

@Getter
@Setter
private boolean enabled;
void setEnabled(boolean status);

}
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@
<bean class="eu.xenit.alfresco.healthprocessor.plugins.solr.filter.DeletedNodeFilter" />

<bean id="eu.xenit.alfresco.healthprocessor.fixer.solr.SolrMissingNodeFixerPlugin"
class="eu.xenit.alfresco.healthprocessor.fixer.solr.SolrMissingNodeFixerPlugin">
class="eu.xenit.alfresco.healthprocessor.fixer.solr.SolrMissingNodeFixerPluginImpl">
<property name="enabled" value="${eu.xenit.alfresco.healthprocessor.fixer.solr-missing-node.enabled}" />
<constructor-arg name="solrRequestExecutor"
ref="eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor" />
</bean>
<bean id="eu.xenit.alfresco.healthprocessor.fixer.solr.SolrDuplicateNodeFixerPlugin"
class="eu.xenit.alfresco.healthprocessor.fixer.solr.SolrDuplicateNodeFixerPlugin">
class="eu.xenit.alfresco.healthprocessor.fixer.solr.SolrDuplicateNodeFixerPluginImpl">
<property name="enabled" value="${eu.xenit.alfresco.healthprocessor.fixer.solr-duplicate-node.enabled}" />
<constructor-arg name="solrRequestExecutor"
ref="eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
import java.util.Set;
import javax.annotation.Nonnull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@AllArgsConstructor
@RequiredArgsConstructor
@Slf4j
abstract class AbstractSolrNodeFixerPlugin extends ToggleableHealthFixerPlugin {
abstract class AbstractSolrNodeFixerPlugin implements ToggleableHealthFixerPlugin {

private final SolrRequestExecutor solrRequestExecutor;
private @Getter @Setter boolean enabled;

@Nonnull
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,18 @@
package eu.xenit.alfresco.healthprocessor.fixer.solr;

import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixReport;
import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixStatus;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport.IndexHealthStatus;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor.SolrNodeCommand;
import eu.xenit.alfresco.healthprocessor.reporter.api.NodeHealthReport;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import eu.xenit.alfresco.healthprocessor.fixer.api.ToggleableHealthFixerPlugin;

public class SolrDuplicateNodeFixerPlugin extends AbstractSolrNodeFixerPlugin {
/**
* <p>
* Interface representation of the {@link SolrDuplicateNodeFixerPluginImpl} class.
* </p>
* <p>
* The reason for tbe existence this interface is to allow the {@link org.alfresco.repo.management.subsystems.SubsystemProxyFactory}
* to create a proxy of the {@link SolrDuplicateNodeFixerPluginImpl} class from the health processor application context
* in the main application context.
* This proxy is only used as part of the integration tests from the health processor repo.
* </p>
*/
public interface SolrDuplicateNodeFixerPlugin extends ToggleableHealthFixerPlugin {

public SolrDuplicateNodeFixerPlugin(SolrRequestExecutor solrRequestExecutor) {
super(solrRequestExecutor);
}

@Override
protected Set<NodeFixReport> handleHealthReport(NodeHealthReport unhealthyReport,
NodeIndexHealthReport endpointHealthReport) {
if (endpointHealthReport.getHealthStatus() != IndexHealthStatus.DUPLICATE) {
return Collections.emptySet();
}
// When a duplicate node is detected, purge it from the index and reindex it
// According to MetadataTracker#maintenance(), purge is processed before reindex
// Even if it is executed in the same maintenance cycle.
// Ref: https://github.com/Alfresco/SearchServices/blob/e7f05e2f13a709cd28afa3ae6acfd3d0000b22ff/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java#L257-L266
// Purge has to be done before reindex, else we end up with a broken index which will only be fixed
// by a subsequent health processor cycle, which would be unacceptable.
Set<NodeFixReport> fixReports = new HashSet<>();
NodeFixReport purgeStatus = trySendSolrCommand(unhealthyReport, endpointHealthReport, SolrNodeCommand.PURGE);
fixReports.add(purgeStatus);
if(purgeStatus.getFixStatus() == NodeFixStatus.SUCCEEDED) {
fixReports.add(trySendSolrCommand(unhealthyReport, endpointHealthReport, SolrNodeCommand.REINDEX));
}

return fixReports;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package eu.xenit.alfresco.healthprocessor.fixer.solr;

import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixReport;
import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixStatus;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport.IndexHealthStatus;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor.SolrNodeCommand;
import eu.xenit.alfresco.healthprocessor.reporter.api.NodeHealthReport;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class SolrDuplicateNodeFixerPluginImpl extends AbstractSolrNodeFixerPlugin implements SolrDuplicateNodeFixerPlugin {

public SolrDuplicateNodeFixerPluginImpl(SolrRequestExecutor solrRequestExecutor) {
super(solrRequestExecutor);
}

@Override
protected Set<NodeFixReport> handleHealthReport(NodeHealthReport unhealthyReport,
NodeIndexHealthReport endpointHealthReport) {
if (endpointHealthReport.getHealthStatus() != IndexHealthStatus.DUPLICATE) {
return Collections.emptySet();
}
// When a duplicate node is detected, purge it from the index and reindex it
// According to MetadataTracker#maintenance(), purge is processed before reindex
// Even if it is executed in the same maintenance cycle.
// Ref: https://github.com/Alfresco/SearchServices/blob/e7f05e2f13a709cd28afa3ae6acfd3d0000b22ff/search-services/alfresco-search/src/main/java/org/alfresco/solr/tracker/MetadataTracker.java#L257-L266
// Purge has to be done before reindex, else we end up with a broken index which will only be fixed
// by a subsequent health processor cycle, which would be unacceptable.
Set<NodeFixReport> fixReports = new HashSet<>();
NodeFixReport purgeStatus = trySendSolrCommand(unhealthyReport, endpointHealthReport, SolrNodeCommand.PURGE);
fixReports.add(purgeStatus);
if(purgeStatus.getFixStatus() == NodeFixStatus.SUCCEEDED) {
fixReports.add(trySendSolrCommand(unhealthyReport, endpointHealthReport, SolrNodeCommand.REINDEX));
}

return fixReports;
}

}
Original file line number Diff line number Diff line change
@@ -1,78 +1,18 @@
package eu.xenit.alfresco.healthprocessor.fixer.solr;

import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixReport;
import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixStatus;
import eu.xenit.alfresco.healthprocessor.plugins.api.HealthProcessorPlugin;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport.IndexHealthStatus;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor.SolrNodeCommand;
import eu.xenit.alfresco.healthprocessor.plugins.solr.endpoint.SearchEndpoint;
import eu.xenit.alfresco.healthprocessor.reporter.api.NodeHealthReport;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.alfresco.service.cmr.repository.NodeRef;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Slf4j
public class SolrMissingNodeFixerPlugin extends AbstractSolrNodeFixerPlugin {
private Map<SearchEndpointTxId, NodeFixReport> searchEndpointTxCache = new HashMap<>();

public SolrMissingNodeFixerPlugin(SolrRequestExecutor solrRequestExecutor) {
super(solrRequestExecutor);
}

@Nonnull
@Override
public Set<NodeFixReport> fix(Class<? extends HealthProcessorPlugin> pluginClass,
Set<NodeHealthReport> unhealthyReports) {
clearCache();
return super.fix(pluginClass, unhealthyReports);
}

@Override
protected Set<NodeFixReport> handleHealthReport(NodeHealthReport unhealthyReport,
NodeIndexHealthReport endpointHealthReport) {
if (endpointHealthReport.getHealthStatus() != IndexHealthStatus.NOT_FOUND) {
return Collections.emptySet();
}

NodeRef.Status nodeStatus = endpointHealthReport.getNodeRefStatus();
SearchEndpointTxId searchEndpointTxId = new SearchEndpointTxId(endpointHealthReport.getEndpoint(), nodeStatus.getDbTxnId());
if (searchEndpointTxCache.containsKey(searchEndpointTxId)) {
NodeFixReport cachedNodeFixReport = searchEndpointTxCache.get(searchEndpointTxId);
log.trace("We already have a fix report for {}: {}", searchEndpointTxId, cachedNodeFixReport);
//If a successful reindex action was already sent for this tx to this endpoint, do not schedule another one
if (cachedNodeFixReport.getFixStatus() == NodeFixStatus.SUCCEEDED) {
log.trace("Fix for TX of {} has already succeeded, sending existing fix report messages.", unhealthyReport);
return Collections.singleton(new NodeFixReport(cachedNodeFixReport.getFixStatus(), unhealthyReport,
cachedNodeFixReport.getMessages()));
}
}

log.trace("Performing reindex for {}", searchEndpointTxId);

// Action not yet (successfully) sent
NodeFixReport nodeFixReport = trySendSolrCommand(unhealthyReport, endpointHealthReport,
SolrNodeCommand.REINDEX_TRANSACTION);

searchEndpointTxCache.put(searchEndpointTxId, nodeFixReport);
return Collections.singleton(nodeFixReport);
}

@Value
public static class SearchEndpointTxId {
private final SearchEndpoint searchEndpoint;
private final Long txId;
}

private void clearCache() {
searchEndpointTxCache.clear();
}
import eu.xenit.alfresco.healthprocessor.fixer.api.ToggleableHealthFixerPlugin;

/**
* <p>
* Interface representation of the {@link SolrMissingNodeFixerPluginImpl} class.
* </p>
* <p>
* The reason for tbe existence this interface is to allow the {@link org.alfresco.repo.management.subsystems.SubsystemProxyFactory}
* to create a proxy of the {@link SolrMissingNodeFixerPluginImpl} class from the health processor application context
* in the main application context.
* This proxy is only used as part of the integration tests from the health processor repo.
* </p>
*/
public interface SolrMissingNodeFixerPlugin extends ToggleableHealthFixerPlugin {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package eu.xenit.alfresco.healthprocessor.fixer.solr;

import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixReport;
import eu.xenit.alfresco.healthprocessor.fixer.api.NodeFixStatus;
import eu.xenit.alfresco.healthprocessor.plugins.api.HealthProcessorPlugin;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport;
import eu.xenit.alfresco.healthprocessor.plugins.solr.NodeIndexHealthReport.IndexHealthStatus;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor;
import eu.xenit.alfresco.healthprocessor.plugins.solr.SolrRequestExecutor.SolrNodeCommand;
import eu.xenit.alfresco.healthprocessor.plugins.solr.endpoint.SearchEndpoint;
import eu.xenit.alfresco.healthprocessor.reporter.api.NodeHealthReport;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.alfresco.service.cmr.repository.NodeRef;

import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Slf4j
public class SolrMissingNodeFixerPluginImpl extends AbstractSolrNodeFixerPlugin implements SolrMissingNodeFixerPlugin {

private Map<SearchEndpointTxId, NodeFixReport> searchEndpointTxCache = new HashMap<>();

public SolrMissingNodeFixerPluginImpl(SolrRequestExecutor solrRequestExecutor) {
super(solrRequestExecutor);
}

@Nonnull
@Override
public Set<NodeFixReport> fix(Class<? extends HealthProcessorPlugin> pluginClass,
Set<NodeHealthReport> unhealthyReports) {
clearCache();
return super.fix(pluginClass, unhealthyReports);
}

@Override
protected Set<NodeFixReport> handleHealthReport(NodeHealthReport unhealthyReport,
NodeIndexHealthReport endpointHealthReport) {
if (endpointHealthReport.getHealthStatus() != IndexHealthStatus.NOT_FOUND) {
return Collections.emptySet();
}

NodeRef.Status nodeStatus = endpointHealthReport.getNodeRefStatus();
SearchEndpointTxId searchEndpointTxId = new SearchEndpointTxId(endpointHealthReport.getEndpoint(), nodeStatus.getDbTxnId());
if (searchEndpointTxCache.containsKey(searchEndpointTxId)) {
NodeFixReport cachedNodeFixReport = searchEndpointTxCache.get(searchEndpointTxId);
log.trace("We already have a fix report for {}: {}", searchEndpointTxId, cachedNodeFixReport);
//If a successful reindex action was already sent for this tx to this endpoint, do not schedule another one
if (cachedNodeFixReport.getFixStatus() == NodeFixStatus.SUCCEEDED) {
log.trace("Fix for TX of {} has already succeeded, sending existing fix report messages.", unhealthyReport);
return Collections.singleton(new NodeFixReport(cachedNodeFixReport.getFixStatus(), unhealthyReport,
cachedNodeFixReport.getMessages()));
}
}

log.trace("Performing reindex for {}", searchEndpointTxId);

// Action not yet (successfully) sent
NodeFixReport nodeFixReport = trySendSolrCommand(unhealthyReport, endpointHealthReport,
SolrNodeCommand.REINDEX_TRANSACTION);

searchEndpointTxCache.put(searchEndpointTxId, nodeFixReport);
return Collections.singleton(nodeFixReport);
}

@Value
public static class SearchEndpointTxId {
private final SearchEndpoint searchEndpoint;
private final Long txId;
}

private void clearCache() {
searchEndpointTxCache.clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@
import org.mockito.InOrder;
import org.mockito.Mockito;

class SolrDuplicateNodeFixerPluginTest {
class SolrDuplicateNodeFixerPluginImplTest {

private SolrRequestExecutor executor;
private SolrDuplicateNodeFixerPlugin duplicateNodeFixerPlugin;
private SolrDuplicateNodeFixerPluginImpl duplicateNodeFixerPlugin;

@BeforeEach
void setup() {
executor = mock(SolrRequestExecutor.class);
duplicateNodeFixerPlugin = new SolrDuplicateNodeFixerPlugin(executor);
duplicateNodeFixerPlugin = new SolrDuplicateNodeFixerPluginImpl(executor);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class SolrMissingNodeFixerPluginTest {
class SolrMissingNodeFixerPluginImplTest {

private SolrRequestExecutor executor;
private SolrMissingNodeFixerPlugin missingNodeFixerPlugin;
private SolrMissingNodeFixerPluginImpl missingNodeFixerPlugin;

@BeforeEach
void setup() {
executor = mock(SolrRequestExecutor.class);
missingNodeFixerPlugin = new SolrMissingNodeFixerPlugin(executor);
missingNodeFixerPlugin = new SolrMissingNodeFixerPluginImpl(executor);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public Optional<Duration> getEstimatedCompletion() {

CycleProgressView indexingProgressView = new CycleProgressView(mockIndexingProgress);

assertThat(indexingProgressView.getProgress(), is(equalTo("10.00%")));
assertThat(indexingProgressView.getProgress(), is(equalTo("10,00%")));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="eu.xenit.alfresco.healthprocessor.example.ExampleHealthProcessorPlugin"
class="eu.xenit.alfresco.healthprocessor.example.ExampleHealthProcessorPlugin">
<constructor-arg name="nodeService" ref="NodeService"/>
<property name="enabled" value="${eu.xenit.alfresco.healthprocessor.plugin.example.enabled}"/>
</bean>


</beans>
Loading

0 comments on commit 3c5c67b

Please sign in to comment.