-
Notifications
You must be signed in to change notification settings - Fork 480
maintenance portlet | Implement fix assets and clean orphan assets polling APIs #35191 #35232
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
Open
hassandotcms
wants to merge
21
commits into
main
Choose a base branch
from
35200-maintenance-fix-assets-clean-assets
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
698b9c4
feat(maintenance): add REST APIs for search/replace, drop old version…
hassandotcms fc9d7c0
test(maintenance): add integration tests for search/replace, old vers…
hassandotcms 65d3572
fix(test): correct exception expectation for empty searchString valid…
hassandotcms f52d55f
chore: add generated openapi.yaml for maintenance endpoints
hassandotcms 7426d2c
feat(maintenance): add REST APIs for fix assets and clean orphan asse…
hassandotcms 054bbe1
chore: update generated openapi.yaml for fix-assets and clean-assets …
hassandotcms 1b1955c
feat(maintenance): add REST APIs for search/replace, drop old version…
hassandotcms e82cfab
test(maintenance): add integration tests for search/replace, old vers…
hassandotcms 2bb8e9c
fix(test): correct exception expectation for empty searchString valid…
hassandotcms fcf313f
chore: add generated openapi.yaml for maintenance endpoints
hassandotcms 6a06d66
fix(maintenance): use UTC for date conversion, add final, remove redu…
hassandotcms 8e5b15b
fix(maintenance): throw 500 on failed dropOldVersions, remove searchS…
hassandotcms fb599c9
docs(maintenance): fix stale deletedCount schema, document partial fa…
hassandotcms ce039be
fix(maintenance): include dateStr in dropOldVersions failure message …
hassandotcms 1fa3e19
Merge branch 'main' of https://github.com/dotCMS/core into 35199-main…
hassandotcms 54a7063
Merge branch '35199-maintenance-search-replace-old-versions-pushed-as…
hassandotcms 27ee06b
Merge branch 'main' of https://github.com/dotCMS/core into 35200-main…
hassandotcms 19a9482
fix(maintenance): harden concurrency and audit logging for fix/clean …
hassandotcms 371cc1d
Merge branch 'main' of https://github.com/dotCMS/core into 35200-main…
hassandotcms c9e3895
refactor(maintenance): migrate fix/clean-assets REST to JobQueueManag…
hassandotcms 991f6f7
Merge branch 'main' of https://github.com/dotCMS/core into 35200-main…
hassandotcms File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
133 changes: 133 additions & 0 deletions
133
dotCMS/src/main/java/com/dotcms/rest/api/v1/maintenance/MaintenanceJobHelper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| package com.dotcms.rest.api.v1.maintenance; | ||
|
|
||
| import com.dotcms.concurrent.DotConcurrentFactory; | ||
| import com.dotcms.concurrent.lock.ClusterLockManager; | ||
| import com.dotcms.jobs.business.api.JobQueueManagerAPI; | ||
| import com.dotcms.jobs.business.job.Job; | ||
| import com.dotcms.jobs.business.job.JobPaginatedResult; | ||
| import com.dotcms.rest.api.v1.job.JobResponseUtil; | ||
| import com.dotcms.rest.api.v1.job.JobStatusResponse; | ||
| import com.dotcms.rest.exception.ConflictException; | ||
| import com.dotmarketing.exception.DotDataException; | ||
| import com.dotmarketing.exception.DotRuntimeException; | ||
| import com.liferay.portal.model.User; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import javax.enterprise.context.ApplicationScoped; | ||
| import javax.inject.Inject; | ||
| import javax.servlet.http.HttpServletRequest; | ||
|
|
||
| /** | ||
| * Creates fix-assets and clean-assets jobs on {@link JobQueueManagerAPI}. A cluster lock | ||
| * around the {@code getActiveJobs} check-and-create gives strict 409 semantics: two nodes | ||
| * cannot both pass the active-job check in the same millisecond. | ||
| * | ||
| * @author hassandotcms | ||
| */ | ||
| @ApplicationScoped | ||
| public class MaintenanceJobHelper { | ||
|
|
||
| public static final String FIX_ASSETS_QUEUE = "maintenanceFixAssets"; | ||
| public static final String CLEAN_ASSETS_QUEUE = "maintenanceCleanAssets"; | ||
|
|
||
| private static final String JOB_STATUS_PATH = "/api/v1/jobs/%s/status"; | ||
| private static final String PARAM_USER_ID = "userId"; | ||
| private static final String PARAM_REMOTE_ADDR = "remoteAddr"; | ||
|
|
||
| private final JobQueueManagerAPI jobQueueManagerAPI; | ||
|
|
||
| @Inject | ||
| public MaintenanceJobHelper(final JobQueueManagerAPI jobQueueManagerAPI) { | ||
| this.jobQueueManagerAPI = jobQueueManagerAPI; | ||
| } | ||
|
|
||
| /** | ||
| * CDI requires a no-arg constructor for proxy creation. | ||
| */ | ||
| public MaintenanceJobHelper() { | ||
| this.jobQueueManagerAPI = null; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a fix-assets job, rejecting with 409 Conflict if one is already running. | ||
| */ | ||
| public JobStatusResponse createFixAssetsJob(final User user, final HttpServletRequest request) { | ||
| return createSingletonJob(FIX_ASSETS_QUEUE, user, request, "fix-assets"); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a clean-orphan-assets job, rejecting with 409 Conflict if one is already running. | ||
| */ | ||
| public JobStatusResponse createCleanAssetsJob(final User user, final HttpServletRequest request) { | ||
| return createSingletonJob(CLEAN_ASSETS_QUEUE, user, request, "clean-assets"); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the latest job for a given queue — active if one is pending or running, otherwise | ||
| * the most recent completed job. Returns {@code null} if no job has ever been created. | ||
| */ | ||
| public Job getLatestJob(final String queueName) { | ||
| try { | ||
| // getJobs() orders by created_at DESC — the first row is the most recent job | ||
| // across all states (pending, running, completed, failed, canceled). | ||
| final JobPaginatedResult result = jobQueueManagerAPI.getJobs(queueName, 1, 1); | ||
| return result.jobs().isEmpty() ? null : result.jobs().get(0); | ||
| } catch (final DotDataException e) { | ||
| throw new DotRuntimeException("Failed to fetch latest job for queue " + queueName, e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Acquires a short-lived cluster lock keyed on the queue name, re-checks the queue for any | ||
| * active job, and enqueues a new job atomically. If the lock cannot be acquired (another node | ||
| * is starting a job in this exact moment) or an active job exists, throws | ||
| * {@link ConflictException}. | ||
| */ | ||
| private JobStatusResponse createSingletonJob( | ||
| final String queueName, | ||
| final User user, | ||
| final HttpServletRequest request, | ||
| final String humanName) { | ||
|
|
||
| final ClusterLockManager<String> lock = | ||
| DotConcurrentFactory.getInstance().getClusterLockManager(queueName); | ||
|
|
||
| final JobStatusResponse result; | ||
| try { | ||
| result = lock.tryClusterLock(() -> | ||
| checkAndEnqueue(queueName, user, request, humanName)); | ||
| } catch (final ConflictException e) { | ||
| throw e; | ||
| } catch (final Throwable t) { | ||
| throw new DotRuntimeException("Failed to create " + humanName + " job", t); | ||
| } | ||
|
|
||
| if (result == null) { | ||
| throw new ConflictException(String.format( | ||
| "Another node is currently starting a %s job; retry in a moment", | ||
| humanName)); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| private JobStatusResponse checkAndEnqueue( | ||
| final String queueName, | ||
| final User user, | ||
| final HttpServletRequest request, | ||
| final String humanName) throws DotDataException { | ||
|
|
||
| final JobPaginatedResult active = jobQueueManagerAPI.getActiveJobs(queueName, 1, 1); | ||
| if (!active.jobs().isEmpty()) { | ||
| throw new ConflictException(String.format( | ||
| "A %s job is already running (jobId=%s)", | ||
| humanName, active.jobs().get(0).id())); | ||
| } | ||
|
|
||
| final Map<String, Object> params = new HashMap<>(); | ||
| params.put(PARAM_USER_ID, user.getUserId()); | ||
| params.put(PARAM_REMOTE_ADDR, request.getRemoteAddr()); | ||
|
|
||
| final String jobId = jobQueueManagerAPI.createJob(queueName, params); | ||
| return JobResponseUtil.buildJobStatusResponse(jobId, JOB_STATUS_PATH, request); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about naming them like :
maintenance/assets/fix
maintenance/assets/clean
or
maintenance/assets/_fix
maintenance/assets/_clean
To please some folks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maintenance/assets/_fix
maintenance/assets/_clean
seems better, consistent with other apis in this file.