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

feat: manage Notifier storage usage [DHIS2-17998] (2.40) #19816

Merged
merged 13 commits into from
Feb 12, 2025
42 changes: 38 additions & 4 deletions dhis-2/dhis-api/src/main/java/org/hisp/dhis/common/UID.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,50 @@
*/
package org.hisp.dhis.common;

import lombok.NoArgsConstructor;
import com.fasterxml.jackson.annotation.JsonCreator;
import java.io.Serializable;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import lombok.EqualsAndHashCode;
import lombok.Getter;

/**
* A "virtual" UID type that is "context-sensitive" and points to a UID of the current {@code
* UID represents an alphanumeric string of 11 characters starting with a letter.
*
* <p>A "virtual" UID type that is "context-sensitive" and points to a UID of the current {@code
* Api.Endpoint}'s {@link org.hisp.dhis.common.OpenApi.EntityType}.
*
* <p>In other words by using this type in {@link OpenApi.Param#value()} the annotated parameter
* becomes a UID string of the controllers' entity type.
*
* @author Jan Bernitt
*/
@NoArgsConstructor
public final class UID {}
@Getter
@EqualsAndHashCode
public final class UID implements Serializable {

private final String value;

private UID(String value) {
if (!CodeGenerator.isValidUid(value)) {
throw new IllegalArgumentException(
"UID must be an alphanumeric string of 11 characters starting with a letter, but was: "
+ value);
}
this.value = value;
}

@Override
public String toString() {
return value;
}

@JsonCreator
public static UID of(@Nonnull String value) {
return new UID(value);
}

public static UID of(@CheckForNull UidObject object) {
return object == null ? null : new UID(object.getUid());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,14 @@ public enum SettingKey {
ANALYTICS_MAX_PERIOD_YEARS_OFFSET("keyAnalyticsPeriodYearsOffset", -1, Integer.class),

/** Max trackedentityinstance records that can be retrieved from database. */
TRACKED_ENTITY_MAX_LIMIT("KeyTrackedEntityInstanceMaxLimit", 50000, Integer.class);
TRACKED_ENTITY_MAX_LIMIT("KeyTrackedEntityInstanceMaxLimit", 50000, Integer.class),

NOTIFIER_LOG_LEVEL("notifierLogLevel", "DEBUG", String.class),
NOTIFIER_MAX_MESSAGES_PER_JOB("notifierMaxMessagesPerJob", 500, Integer.class),
NOTIFIER_MAX_AGE_DAYS("notifierMaxAgeDays", 7, Integer.class),
NOTIFIER_MAX_JOBS_PER_TYPE("notifierMaxJobsPerType", 500, Integer.class),
NOTIFIER_GIST_OVERVIEW("notifierGistOverview", true, Boolean.class),
NOTIFIER_CLEAN_AFTER_IDLE_TIME("notifierCleanAfterIdleTime", 60_000L, Long.class);

private final String name;

Expand Down Expand Up @@ -324,6 +331,8 @@ public static Serializable getAsRealClass(String name, String value) {
return Double.valueOf(value);
} else if (Integer.class.isAssignableFrom(settingClazz)) {
return Integer.valueOf(value);
} else if (Long.class.isAssignableFrom(settingClazz)) {
return Long.valueOf(value);
} else if (Boolean.class.isAssignableFrom(settingClazz)) {
return Boolean.valueOf(value);
} else if (Locale.class.isAssignableFrom(settingClazz)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2004-2025, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.setting;

import java.io.Serializable;

public interface SystemSettingsProvider {

<T extends Serializable> T getSystemSetting(SettingKey key, Class<T> type);

/**
* Returns the system setting value for the given key. If no value exists, returns the default
* value as defined by the given default value.
*
* @param key the system setting key.
* @return the setting value.
*/
<T extends Serializable> T getSystemSetting(SettingKey key, T defaultValue);

String getStringSetting(SettingKey key);

Integer getIntegerSetting(SettingKey key);

int getIntSetting(SettingKey key);

Boolean getBooleanSetting(SettingKey key);

boolean getBoolSetting(SettingKey key);
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.hisp.dhis.leader.election.LeaderManager;
import org.hisp.dhis.message.MessageService;
import org.hisp.dhis.scheduling.JobProgress.Process;
import org.hisp.dhis.system.notification.NotificationLevel;
import org.hisp.dhis.system.notification.Notifier;
import org.hisp.dhis.system.util.Clock;
import org.slf4j.MDC;
Expand Down Expand Up @@ -340,7 +341,7 @@ public boolean checkWasSuccessfulRun(
private ControlledJobProgress createJobProgress(JobConfiguration configuration) {
JobProgress tracker =
configuration.getJobType().isUsingNotifications()
? new NotifierJobProgress(notifier, configuration)
? new NotifierJobProgress(notifier, configuration, NotificationLevel.DEBUG)
: NoopJobProgress.INSTANCE;
return new ControlledJobProgress(messageService, configuration, tracker, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import lombok.RequiredArgsConstructor;
import org.hisp.dhis.system.notification.NotificationDataType;
import org.hisp.dhis.system.notification.NotificationLevel;
Expand All @@ -45,28 +47,40 @@
*/
@RequiredArgsConstructor
public class NotifierJobProgress implements JobProgress {
private final Notifier notifier;

private final Notifier notifier;
private final JobConfiguration jobId;

private final NotificationLevel level;
private final AtomicBoolean hasCleared = new AtomicBoolean();

private int stageItems;

private int stageItem;

@Override
public boolean isCancellationRequested() {
return false;
}

private boolean isLoggedLoop() {
return NotificationLevel.LOOP.ordinal() >= level.ordinal();
}

private boolean isLoggedInfo() {
return NotificationLevel.INFO.ordinal() >= level.ordinal();
}

private boolean isLoggedError() {
return NotificationLevel.ERROR.ordinal() >= level.ordinal();
}

@Override
public void startingProcess(String description) {
String message =
isNotEmpty(description) ? description : jobId.getJobType() + " process started";
if (hasCleared.compareAndSet(false, true)) {
notifier.clear(jobId);
}
// Note: intentionally no log level check - always log first
notifier.notify(
jobId,
NotificationLevel.INFO,
Expand All @@ -78,40 +92,43 @@ public void startingProcess(String description) {

@Override
public void completedProcess(String summary) {
// Note: intentionally no log level check - always log last
notifier.notify(jobId, summary, true);
}

@Override
public void failedProcess(String error) {
public void failedProcess(@CheckForNull String error) {
// Note: intentionally no log level check - always log last
notifier.notify(jobId, NotificationLevel.ERROR, error, true);
}

@Override
public void startingStage(String description, int workItems, FailurePolicy onFailure) {
public void startingStage(
@Nonnull String description, int workItems, @Nonnull FailurePolicy onFailure) {
stageItems = workItems;
stageItem = 0;
if (isNotEmpty(description)) {
if (isLoggedInfo() && isNotEmpty(description)) {
notifier.notify(jobId, description);
}
}

@Override
public void completedStage(String summary) {
if (isNotEmpty(summary)) {
if (isLoggedInfo() && isNotEmpty(summary)) {
notifier.notify(jobId, summary);
}
}

@Override
public void failedStage(String error) {
if (isNotEmpty(error)) {
public void failedStage(@Nonnull String error) {
if (isLoggedError() && isNotEmpty(error)) {
notifier.notify(jobId, NotificationLevel.ERROR, error, false);
}
}

@Override
public void startingWorkItem(String description, FailurePolicy onFailure) {
if (isNotEmpty(description)) {
public void startingWorkItem(@Nonnull String description, @Nonnull FailurePolicy onFailure) {
if (isLoggedLoop() && isNotEmpty(description)) {
String nOf = "[" + (stageItems > 0 ? stageItem + "/" + stageItems : "" + stageItem) + "] ";
notifier.notify(jobId, NotificationLevel.LOOP, nOf + description, false);
}
Expand All @@ -120,15 +137,15 @@ public void startingWorkItem(String description, FailurePolicy onFailure) {

@Override
public void completedWorkItem(String summary) {
if (isNotEmpty(summary)) {
if (isLoggedLoop() && isNotEmpty(summary)) {
String nOf = "[" + (stageItems > 0 ? stageItem + "/" + stageItems : "" + stageItem) + "] ";
notifier.notify(jobId, NotificationLevel.LOOP, nOf + summary, false);
}
}

@Override
public void failedWorkItem(String error) {
if (isNotEmpty(error)) {
public void failedWorkItem(@Nonnull String error) {
if (isLoggedError() && isNotEmpty(error)) {
notifier.notify(jobId, NotificationLevel.ERROR, error, false);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ private ImportSummary importDataValueSet(
ImportOptions options, JobConfiguration id, Callable<DataValueSetReader> createReader) {
options = ObjectUtils.firstNonNull(options, ImportOptions.getDefaultImportOptions());

notifier.clear(id);
if (id != null) notifier.clear(id);

try (BatchHandler<DataValue> dvBatch =
batchHandlerFactory.createBatchHandler(DataValueBatchHandler.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public void setUp() {
any(JobConfiguration.class), any(NotificationLevel.class), anyString(), anyBoolean()))
.thenReturn(notifier);
when(notifier.notify(any(JobConfiguration.class), anyString())).thenReturn(notifier);
when(notifier.clear(any())).thenReturn(notifier);
when(notifier.clear(any(JobConfiguration.class))).thenReturn(notifier);

when(defaultTrackedEntityInstanceService.getTrackedEntityInstance(
trackedEntityInstanceUid, user))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ void testImportDataValuesUpdatedSkipNoChange() {
when(batchHandlerFactory.createBatchHandler(DataValueAuditBatchHandler.class))
.thenReturn(auditBatchHandler);

when(notifier.clear(any())).thenReturn(notifier);
when(notifier.notify(any(), any(), anyString())).thenReturn(notifier);
when(notifier.notify(any(), any(), anyString(), anyBoolean())).thenReturn(notifier);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* @author Stian Strandli
* @author Lars Helge Overland
*/
public interface SystemSettingManager {
public interface SystemSettingManager extends SystemSettingsProvider {
/**
* Saves the given system setting key and value.
*
Expand Down Expand Up @@ -74,6 +74,7 @@ public interface SystemSettingManager {
* @param key the system setting key.
* @return the setting value.
*/
@Override
default <T extends Serializable> T getSystemSetting(SettingKey key, Class<T> type) {
if (type != key.getClazz()) {
throw new IllegalArgumentException(
Expand All @@ -84,15 +85,6 @@ default <T extends Serializable> T getSystemSetting(SettingKey key, Class<T> typ
return type.cast(getSystemSetting(key, key.getDefaultValue()));
}

/**
* Returns the system setting value for the given key. If no value exists, returns the default
* value as defined by the given default value.
*
* @param key the system setting key.
* @return the setting value.
*/
<T extends Serializable> T getSystemSetting(SettingKey key, T defaultValue);

/**
* Returns the translation for given setting key and locale or empty Optional if no translation is
* available or setting key is not translatable.
Expand Down Expand Up @@ -138,22 +130,27 @@ default <T extends Serializable> T getSystemSetting(SettingKey key, Class<T> typ
// Typed methods
// -------------------------------------------------------------------------

@Override
default String getStringSetting(SettingKey key) {
return getSystemSetting(key, String.class);
}

@Override
default Integer getIntegerSetting(SettingKey key) {
return getSystemSetting(key, Integer.class);
}

@Override
default int getIntSetting(SettingKey key) {
return getSystemSetting(key, Integer.class);
}

@Override
default Boolean getBooleanSetting(SettingKey key) {
return getSystemSetting(key, Boolean.class);
}

@Override
default boolean getBoolSetting(SettingKey key) {
return Boolean.TRUE.equals(getSystemSetting(key, Boolean.class));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public Stats getStats() {
return stats;
}

@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public boolean isEmpty() {
return typeReportMap.values().stream().allMatch(TrackerTypeReport::isEmpty);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class Stats {

@JsonProperty private int ignored = 0;

@JsonProperty
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public int getTotal() {
return created + updated + deleted + ignored;
}
Expand Down
Loading