From 31e3616e1408857f88a8f7d2f3b372d0b9ddcef2 Mon Sep 17 00:00:00 2001 From: "youngjin.kim2" Date: Tue, 23 Jan 2024 17:53:02 +0900 Subject: [PATCH] [#10605] Added cleanup inactive applications batch job --- .../navercorp/pinpoint/batch/BatchModule.java | 15 +++ .../batch/common/BatchJobLauncher.java | 20 +++- .../batch/common/BatchProperties.java | 16 ++++ .../batch/common/StartupJobLauncher.java | 67 +++++++++++++ .../CleanupInactiveApplicationsJobConfig.java | 28 ++++++ .../pinpoint/batch/job/AgentCountReader.java | 5 +- .../batch/job/ApplicationEmptyFilter.java | 57 ++++++++++++ .../pinpoint/batch/job/ApplicationReader.java | 88 ++++++++++++++++++ .../batch/job/ApplicationRemover.java | 48 ++++++++++ .../pinpoint/batch/job/EmptyItemWriter.java | 34 +++++++ .../batch/service/ApplicationService.java | 33 +++++++ .../batch/service/ApplicationServiceImpl.java | 93 +++++++++++++++++++ .../applicationContext-batch-schedule.xml | 1 + .../src/main/resources/batch-root.properties | 8 ++ ...Context-cleanupInactiveApplicationsJob.xml | 58 ++++++++++++ 15 files changed, 564 insertions(+), 7 deletions(-) create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/common/StartupJobLauncher.java create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/configuration/CleanupInactiveApplicationsJobConfig.java create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationEmptyFilter.java create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationReader.java create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationRemover.java create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/job/EmptyItemWriter.java create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationService.java create mode 100644 batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationServiceImpl.java create mode 100644 batch/src/main/resources/job/applicationContext-cleanupInactiveApplicationsJob.xml diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/BatchModule.java b/batch/src/main/java/com/navercorp/pinpoint/batch/BatchModule.java index c8fc5838fbb7..832c36fa9891 100644 --- a/batch/src/main/java/com/navercorp/pinpoint/batch/BatchModule.java +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/BatchModule.java @@ -18,7 +18,10 @@ package com.navercorp.pinpoint.batch; import com.navercorp.pinpoint.batch.alarm.AlarmSenderConfiguration; +import com.navercorp.pinpoint.batch.common.BatchJobLauncher; +import com.navercorp.pinpoint.batch.common.StartupJobLauncher; import com.navercorp.pinpoint.batch.configuration.AlarmJobModule; +import com.navercorp.pinpoint.batch.configuration.CleanupInactiveApplicationsJobConfig; import com.navercorp.pinpoint.common.server.config.CommonCacheManagerConfiguration; import com.navercorp.pinpoint.common.server.config.RestTemplateConfiguration; import com.navercorp.pinpoint.common.server.util.DefaultTimeSlot; @@ -31,11 +34,14 @@ import com.navercorp.pinpoint.web.component.config.ComponentConfiguration; import com.navercorp.pinpoint.web.hyperlink.HyperLinkConfiguration; import com.navercorp.pinpoint.web.webhook.WebhookModule; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; +import java.util.List; + @ImportResource({ "classpath:applicationContext-batch-schedule.xml", @@ -67,6 +73,7 @@ UriStatAlarmConfiguration.class, AlarmSenderConfiguration.class, CommonCacheManagerConfiguration.class, + CleanupInactiveApplicationsJobConfig.class, }) public class BatchModule { @@ -74,4 +81,12 @@ public class BatchModule { public TimeSlot timeSlot() { return new DefaultTimeSlot(); } + + @Bean + StartupJobLauncher startupJobLauncher( + BatchJobLauncher launcher, + @Value("${batch.startup.jobs:#{''}}") List jobs + ) { + return new StartupJobLauncher(launcher, jobs); + } } \ No newline at end of file diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchJobLauncher.java b/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchJobLauncher.java index f2ceffb0f29b..bac41c462885 100644 --- a/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchJobLauncher.java +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchJobLauncher.java @@ -16,8 +16,8 @@ package com.navercorp.pinpoint.batch.common; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.configuration.JobLocator; @@ -36,8 +36,11 @@ public class BatchJobLauncher extends JobLaunchSupport { private final BatchProperties batchProperties; - public BatchJobLauncher(@Qualifier("jobRegistry") JobLocator locator, - @Qualifier("jobLauncher") JobLauncher launcher, BatchProperties batchProperties) { + public BatchJobLauncher( + @Qualifier("jobRegistry") JobLocator locator, + @Qualifier("jobLauncher") JobLauncher launcher, + BatchProperties batchProperties + ) { super(locator, launcher); this.batchProperties = Objects.requireNonNull(batchProperties, "batchProperties"); } @@ -59,7 +62,7 @@ public void uriStatAlarmJob() { } } - private JobParameters createTimeParameter() { + public static JobParameters createTimeParameter() { JobParametersBuilder builder = new JobParametersBuilder(); Date now = new Date(); builder.addDate("schedule.date", now); @@ -89,4 +92,13 @@ public void cleanupInactiveAgentsJob() { logger.debug("Skip cleanupInactiveAgentsJob, because 'enableCleanupInactiveAgentsJob' is disabled."); } } + + public void cleanupInactiveApplicationsJob() { + if (batchProperties.isCleanupInactiveApplicationsJobEnable()) { + run("cleanupInactiveApplicationsJob", createTimeParameter()); + } else { + logger.debug("Skip applicationCleanJob, because 'enableCleanupInactiveApplicationsJob' is disabled."); + } + } + } diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchProperties.java b/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchProperties.java index 997eb703df3d..0a2d85db6204 100644 --- a/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchProperties.java +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/common/BatchProperties.java @@ -76,6 +76,12 @@ public class BatchProperties { @Value("${job.cleanup.inactive.agents.duration.days:30}") private int cleanupInactiveAgentsDurationDays; + @Value("${job.cleanup.inactive.applications.enable:false}") + private boolean cleanupInactiveApplicationsJobEnable; + + @Value("${job.cleanup.inactive.applications.cron}") + private String cleanupInactiveApplicationsJobCron; + private static final int MINIMUM_CLEANUP_INACTIVE_AGENTS_DURATION_DAYS = 7; @PostConstruct @@ -156,6 +162,14 @@ public int getCleanupInactiveAgentsDurationDays() { return cleanupInactiveAgentsDurationDays; } + public boolean isCleanupInactiveApplicationsJobEnable() { + return cleanupInactiveApplicationsJobEnable; + } + + public String getCleanupInactiveApplicationsJobCron() { + return cleanupInactiveApplicationsJobCron; + } + @Override public String toString() { return "BatchProperties{" + @@ -174,6 +188,8 @@ public String toString() { ", uriStatAlarmJobEnable=" + uriStatAlarmJobEnable + ", uriStatAlarmJobCron='" + uriStatAlarmJobCron + '\'' + ", cleanupInactiveAgentsDurationDays=" + cleanupInactiveAgentsDurationDays + + ", cleanupInactiveApplicationsJobEnable=" + cleanupInactiveApplicationsJobEnable + + ", cleanupInactiveApplicationsJobCron='" + cleanupInactiveApplicationsJobCron + '\'' + '}'; } } diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/common/StartupJobLauncher.java b/batch/src/main/java/com/navercorp/pinpoint/batch/common/StartupJobLauncher.java new file mode 100644 index 000000000000..0a774b2eac1b --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/common/StartupJobLauncher.java @@ -0,0 +1,67 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.common; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author youngjin.kim2 + */ +public class StartupJobLauncher implements InitializingBean, DisposableBean { + + private static final Logger logger = LogManager.getLogger(StartupJobLauncher.class); + + private final BatchJobLauncher launcher; + private final List jobs; + + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + + public StartupJobLauncher(BatchJobLauncher launcher, List jobs) { + this.launcher = Objects.requireNonNull(launcher, "launcher"); + this.jobs = Objects.requireNonNull(jobs, "jobs"); + } + + @Override + public void afterPropertiesSet() { + if (this.jobs.isEmpty()) { + logger.info("No startup jobs to launch"); + return; + } + + logger.info("Startup job launcher started"); + this.executor.execute(() -> { + for (String job : jobs) { + logger.info("Launching job {}", job); + launcher.run(job, BatchJobLauncher.createTimeParameter()); + } + logger.info("Startup job launcher finished"); + }); + } + + @Override + public void destroy() { + this.executor.shutdownNow(); + } +} diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/configuration/CleanupInactiveApplicationsJobConfig.java b/batch/src/main/java/com/navercorp/pinpoint/batch/configuration/CleanupInactiveApplicationsJobConfig.java new file mode 100644 index 000000000000..4e9cb6bcb3e7 --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/configuration/CleanupInactiveApplicationsJobConfig.java @@ -0,0 +1,28 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.configuration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; + +/** + * @author youngjin.kim2 + */ +@ImportResource({ "classpath:job/applicationContext-cleanupInactiveApplicationsJob.xml" }) +@Configuration(proxyBeanMethods = false) +public class CleanupInactiveApplicationsJobConfig { +} diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/job/AgentCountReader.java b/batch/src/main/java/com/navercorp/pinpoint/batch/job/AgentCountReader.java index cd8477642578..807a2a1a920d 100644 --- a/batch/src/main/java/com/navercorp/pinpoint/batch/job/AgentCountReader.java +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/job/AgentCountReader.java @@ -18,17 +18,16 @@ import com.navercorp.pinpoint.web.dao.ApplicationIndexDao; import com.navercorp.pinpoint.web.vo.Application; +import jakarta.annotation.Nonnull; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.StepExecutionListener; import org.springframework.batch.item.ItemReader; -import jakarta.annotation.Nonnull; import java.util.List; import java.util.Objects; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.stream.Collectors; /** * @author youngjin.kim2 @@ -48,7 +47,7 @@ public void beforeStep(@Nonnull StepExecution stepExecution) { List applicationNames = applicationIndexDao.selectAllApplicationNames() .stream() .map(Application::getName) - .collect(Collectors.toUnmodifiableList()); + .toList(); this.applicationNameQueue = new ConcurrentLinkedQueue<>(applicationNames); } diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationEmptyFilter.java b/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationEmptyFilter.java new file mode 100644 index 000000000000..6d83d5a6ab20 --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationEmptyFilter.java @@ -0,0 +1,57 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.job; + +import com.navercorp.pinpoint.batch.service.ApplicationService; +import jakarta.annotation.Nonnull; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.batch.item.ItemProcessor; + +import java.time.Duration; +import java.util.Objects; + +/** + * @author youngjin.kim2 + */ +public class ApplicationEmptyFilter implements ItemProcessor { + + private static final Logger logger = LogManager.getLogger(ApplicationEmptyFilter.class); + + private final ApplicationService applicationService; + private final Duration emptyDurationThreshold; + + public ApplicationEmptyFilter(ApplicationService applicationService, Duration emptyDurationThreshold) { + this.applicationService = Objects.requireNonNull(applicationService, "applicationService"); + this.emptyDurationThreshold = Objects.requireNonNull(emptyDurationThreshold, "emptyDurationThreshold"); + } + + @Override + public String process(@Nonnull String s) throws Exception { + if (isApplicationEmpty(s)) { + logger.info("Application is empty: {}", s); + return s; + } else { + logger.info("Application is not empty: {}", s); + return null; + } + } + + private boolean isApplicationEmpty(String applicationName) { + return this.applicationService.isApplicationEmpty(applicationName, this.emptyDurationThreshold); + } +} diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationReader.java b/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationReader.java new file mode 100644 index 000000000000..7d52e9cbe6b2 --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationReader.java @@ -0,0 +1,88 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.job; + +import com.navercorp.pinpoint.batch.service.ApplicationService; +import jakarta.annotation.Nonnull; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.ItemStreamException; +import org.springframework.batch.item.ItemStreamReader; + +import java.util.List; +import java.util.Objects; + +/** + * @author youngjin.kim2 + */ +public class ApplicationReader implements ItemStreamReader { + + private static final Logger logger = LogManager.getLogger(ApplicationReader.class); + private static final String CURRENT_INDEX = "current.index"; + + private final ApplicationService applicationService; + + private List applicationNames; + int currentIndex = 0; + + public ApplicationReader(ApplicationService applicationService) { + this.applicationService = Objects.requireNonNull(applicationService, "applicationService"); + } + + @Override + public String read() { + if (currentIndex < applicationNames.size()) { + logger.info("Reading application: {} / {}", currentIndex, applicationNames.size()); + return applicationNames.get(currentIndex++); + } else { + return null; + } + } + + @Override + public void open(@Nonnull ExecutionContext executionContext) throws ItemStreamException { + logger.info("Opened application reader"); + this.applicationNames = getAllApplications(); + logger.info("Application reader has {} applications", applicationNames.size()); + if (executionContext.containsKey(CURRENT_INDEX)) { + this.currentIndex = executionContext.getInt(CURRENT_INDEX); + logger.info("Application reader starts from index {}", currentIndex); + } else { + this.currentIndex = 0; + logger.info("Application reader starts from beginning"); + } + } + + @Override + public void update(@Nonnull ExecutionContext executionContext) throws ItemStreamException { + executionContext.putInt(CURRENT_INDEX, this.currentIndex); + } + + @Override + public void close() throws ItemStreamException { + logger.info("Closing application reader"); + } + + private List getAllApplications() { + return this.applicationService.getApplicationNames() + .stream() + .sorted() + .toList(); + } + +} diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationRemover.java b/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationRemover.java new file mode 100644 index 000000000000..c2afce2afc92 --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/job/ApplicationRemover.java @@ -0,0 +1,48 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.job; + +import com.navercorp.pinpoint.batch.service.ApplicationService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.batch.item.ItemWriter; + +import java.util.List; +import java.util.Objects; + +/** + * @author youngjin.kim2 + */ +public class ApplicationRemover implements ItemWriter { + + private static final Logger logger = LogManager.getLogger(ApplicationRemover.class); + + private final ApplicationService applicationService; + + public ApplicationRemover(ApplicationService applicationService) { + this.applicationService = Objects.requireNonNull(applicationService, "applicationService"); + } + + @Override + public void write(List list) throws Exception { + for (String applicationName : list) { + logger.info("Removing application: {}", applicationName); + this.applicationService.removeApplication(applicationName); + logger.info("Removed application: {}", applicationName); + } + } +} diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/job/EmptyItemWriter.java b/batch/src/main/java/com/navercorp/pinpoint/batch/job/EmptyItemWriter.java new file mode 100644 index 000000000000..70bcbd39665b --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/job/EmptyItemWriter.java @@ -0,0 +1,34 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.job; + +import jakarta.annotation.Nonnull; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.batch.item.ItemWriter; + +import java.util.List; + +public class EmptyItemWriter implements ItemWriter { + + private static final Logger logger = LogManager.getLogger(EmptyItemWriter.class); + + @Override + public void write(@Nonnull List items) throws Exception { + logger.info("Write items: {}", items); + } +} diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationService.java b/batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationService.java new file mode 100644 index 000000000000..24ce7aded4c1 --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationService.java @@ -0,0 +1,33 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.service; + +import java.time.Duration; +import java.util.List; + +/** + * @author youngjin.kim2 + */ +public interface ApplicationService { + + List getApplicationNames(); + + boolean isApplicationEmpty(String applicationName, Duration duration); + + void removeApplication(String applicationName); + +} diff --git a/batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationServiceImpl.java b/batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationServiceImpl.java new file mode 100644 index 000000000000..38753e4bfa89 --- /dev/null +++ b/batch/src/main/java/com/navercorp/pinpoint/batch/service/ApplicationServiceImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright 2024 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.batch.service; + +import com.navercorp.pinpoint.common.profiler.util.TransactionId; +import com.navercorp.pinpoint.common.server.util.time.Range; +import com.navercorp.pinpoint.web.dao.ApplicationIndexDao; +import com.navercorp.pinpoint.web.dao.ApplicationTraceIndexDao; +import com.navercorp.pinpoint.web.service.AgentInfoService; +import com.navercorp.pinpoint.web.vo.Application; +import com.navercorp.pinpoint.web.vo.LimitedScanResult; +import com.navercorp.pinpoint.web.vo.agent.AgentStatusFilter; +import com.navercorp.pinpoint.web.vo.agent.AgentStatusFilterChain; +import com.navercorp.pinpoint.web.vo.tree.AgentsMapByHost; +import com.navercorp.pinpoint.web.vo.tree.SortByAgentInfo.Rules; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.List; +import java.util.Objects; + +/** + * @author youngjin.kim2 + */ +@Service +public class ApplicationServiceImpl implements ApplicationService { + + private static final Range INF_RANGE = Range.between(0L, Long.MAX_VALUE); + + private final ApplicationIndexDao applicationIndexDao; + private final ApplicationTraceIndexDao applicationTraceIndexDao; + private final AgentInfoService agentInfoService; + + public ApplicationServiceImpl( + ApplicationIndexDao applicationIndexDao, + ApplicationTraceIndexDao applicationTraceIndexDao, + AgentInfoService agentInfoService + ) { + this.applicationIndexDao = Objects.requireNonNull(applicationIndexDao, "applicationIndexDao"); + this.applicationTraceIndexDao = Objects.requireNonNull(applicationTraceIndexDao, "applicationTraceIndexDao"); + this.agentInfoService = Objects.requireNonNull(agentInfoService, "agentInfoService"); + } + + @Override + public List getApplicationNames() { + return this.applicationIndexDao.selectAllApplicationNames() + .stream() + .map(Application::getName) + .toList(); + } + + @Override + public boolean isApplicationEmpty(String applicationName, Duration duration) { + long now = System.currentTimeMillis(); + Range range = Range.between(now - duration.toMillis(), now); + return !isAliveAgentExist(applicationName, range) && !isTraceExist(applicationName, range); + } + + private boolean isAliveAgentExist(String applicationName, Range range) { + AgentsMapByHost agents = this.agentInfoService.getAgentsListByApplicationName( + new AgentStatusFilterChain(AgentStatusFilter::filterRunning), + applicationName, + range, + Rules.AGENT_NAME_ASC + ); + return !agents.getAgentsListsList().isEmpty(); + } + + private boolean isTraceExist(String applicationName, Range range) { + LimitedScanResult> result = this.applicationTraceIndexDao.scanTraceIndex( + applicationName, range, 1, false); + return !result.getScanData().isEmpty(); + } + + @Override + public void removeApplication(String applicationName) { + this.applicationIndexDao.deleteApplicationName(applicationName); + } +} diff --git a/batch/src/main/resources/applicationContext-batch-schedule.xml b/batch/src/main/resources/applicationContext-batch-schedule.xml index ac24af0d8dd6..2c591e554675 100644 --- a/batch/src/main/resources/applicationContext-batch-schedule.xml +++ b/batch/src/main/resources/applicationContext-batch-schedule.xml @@ -12,6 +12,7 @@ + diff --git a/batch/src/main/resources/batch-root.properties b/batch/src/main/resources/batch-root.properties index c5a19e8c6ed9..b10921b45cd5 100644 --- a/batch/src/main/resources/batch-root.properties +++ b/batch/src/main/resources/batch-root.properties @@ -41,6 +41,14 @@ job.cleanup.inactive.agents.enable=false job.cleanup.inactive.agents.cron=0 0 3 * * WED job.cleanup.inactive.agents.duration.days=30 +job.cleanup.inactive.applications.enable=true +job.cleanup.inactive.applications.cron=0 0 3 * * THU +job.cleanup.inactive.applications.emptydurationthreshold=P14D + +# - emptyItemWriter: Not actually removes application +# - applicationRemover: Actually removes application +job.cleanup.inactive.applications.writer=emptyItemWriter + ########################################################### # BANNER # ########################################################### diff --git a/batch/src/main/resources/job/applicationContext-cleanupInactiveApplicationsJob.xml b/batch/src/main/resources/job/applicationContext-cleanupInactiveApplicationsJob.xml new file mode 100644 index 000000000000..040fd0188c91 --- /dev/null +++ b/batch/src/main/resources/job/applicationContext-cleanupInactiveApplicationsJob.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +