-
Notifications
You must be signed in to change notification settings - Fork 92
feat(compression-coordinator): Introduce max_concurrent_tasks config to limit job-handler concurrency.
#2435
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
Bill-hbrhbr
wants to merge
27
commits into
y-scope:main
Choose a base branch
from
Bill-hbrhbr:coordinator/limit-job-submission-concurrency
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 8 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
739dd2a
First implementation
Bill-hbrhbr 51ede0e
Merge branch 'main' into coordinator/limit-job-submission-concurrency
Bill-hbrhbr fb1a960
Merge branch 'main' into coordinator/limit-job-submission-concurrency
Bill-hbrhbr 4830f76
Merge branch 'main' into coordinator/limit-job-submission-concurrency
Bill-hbrhbr 2d83f7b
Pass max concurrency limit through clp config. Complete clp config te…
Bill-hbrhbr 95074ce
revise docstring and rename variables
Bill-hbrhbr 2fb4d8a
Fix syntax and docstrings
Bill-hbrhbr e083517
Minor improvements
Bill-hbrhbr a256362
Add invalid config error for exceeding sem max
Bill-hbrhbr ccebe54
Clarify that the concurrency limit may be exceeded with extended time…
Bill-hbrhbr 46e219c
Update config wording
Bill-hbrhbr 46c47f9
Merge branch 'main' into coordinator/limit-job-submission-concurrency
Bill-hbrhbr c6ea927
Merge branch 'main' into coordinator/limit-job-submission-concurrency
LinZhihao-723 a75016f
First implementation
Bill-hbrhbr 191d127
Remove the config for docker compose as it is not ready yet
Bill-hbrhbr 0986378
Address review comment by using bounded fetch
Bill-hbrhbr eee03da
recover not only running jobs but also dispatched jobs
Bill-hbrhbr 96de148
Always take a limit argument for fetch_new_job_rows
Bill-hbrhbr 283018f
Improve docstring
Bill-hbrhbr e044371
Fix docstrings and make create_job_handle return Some instead of Resu…
Bill-hbrhbr 1bcf75a
Change max_concurrent_tasks to max_concurrent_jobs
Bill-hbrhbr 6aa5533
Add job handler dispatch time update
Bill-hbrhbr dad216c
Merge branch 'coordinator/ensure-running-job-with-dispatch-time' into…
Bill-hbrhbr c3f6f16
Merge branch 'main' into coordinator/ensure-running-job-with-dispatch…
Bill-hbrhbr 1df15e1
Update components/compression-coordinator/src/coordination.rs
Bill-hbrhbr 10eb46a
Merge branch 'coordinator/ensure-running-job-with-dispatch-time' into…
Bill-hbrhbr 87365af
Redo design
Bill-hbrhbr 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
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
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
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 |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| //! The coordinator poll loop that discovers pending CLP compression jobs and dispatches them to | ||
| //! Spider. | ||
|
|
||
| use std::collections::VecDeque; | ||
| use std::sync::Arc; | ||
| use std::time::Duration; | ||
|
|
||
|
|
@@ -19,6 +20,7 @@ use spider_core::task::TimeoutPolicy; | |
| use spider_core::types::id::JobId as SpiderJobId; | ||
| use spider_core::types::id::ResourceGroupId; | ||
| use tokio::select; | ||
| use tokio::sync::Semaphore; | ||
| use tokio::time::Instant; | ||
| use tokio_util::sync::CancellationToken; | ||
| use tonic::transport::Endpoint; | ||
|
|
@@ -37,14 +39,17 @@ pub struct Coordinator { | |
| is_first_fetch: bool, | ||
| job_polling_interval: Duration, | ||
| cancellation_token: CancellationToken, | ||
| job_handler_sem: Arc<Semaphore>, | ||
| pending_job_queue: VecDeque<PendingJobRowProjection>, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if we need this. Can you explain why we can't do the following instead:
This should lead to the behavior we expect iiuc. |
||
| } | ||
|
|
||
| impl Coordinator { | ||
| /// Factory function. | ||
| /// | ||
| /// On construction, this recovers compression jobs that a previous coordinator instance had | ||
| /// already submitted to Spider (those still [`CompressionJobStatus::Running`] with a Spider job | ||
| /// ID) by spawning a detached handle to drive each one to completion. | ||
| /// On construction, this begins recovering all compression jobs that a previous coordinator | ||
| /// instance had already submitted to Spider (those still [`CompressionJobStatus::Running`] with | ||
| /// a Spider job ID). During the restart phase, no concurrency limit is imposed, so all | ||
| /// recovered jobs are resumed immediately. | ||
|
Bill-hbrhbr marked this conversation as resolved.
Outdated
|
||
| /// | ||
| /// # Returns | ||
| /// | ||
|
|
@@ -116,6 +121,7 @@ impl Coordinator { | |
| }); | ||
|
|
||
| let cancellation_token = CancellationToken::new(); | ||
| let max_concurrent_tasks = coordinator_config.max_concurrent_tasks.get(); | ||
|
|
||
| let coordinator = Self { | ||
| resource_group_id, | ||
|
|
@@ -128,6 +134,8 @@ impl Coordinator { | |
| coordinator_config.job_polling_interval_millisecs.get(), | ||
| ), | ||
| cancellation_token: cancellation_token.clone(), | ||
| job_handler_sem: Arc::new(Semaphore::new(max_concurrent_tasks)), | ||
| pending_job_queue: VecDeque::new(), | ||
| }; | ||
|
|
||
| for (job_id, spider_job_id, clp_io_config) in | ||
|
|
@@ -141,7 +149,12 @@ impl Coordinator { | |
| let Ok(job_handle) = coordinator.create_job_handle(job_id, clp_io_config).await else { | ||
| continue; | ||
| }; | ||
|
|
||
| // Try to acquire a permit, but still spawn the recovery task if none is available. | ||
| let permit = coordinator.job_handler_sem.clone().try_acquire_owned().ok(); | ||
|
|
||
| tokio::spawn(async move { | ||
| let _permit = permit; | ||
| let _ = job_handle.recover(spider_job_id).await.inspect_err(|e| { | ||
| tracing::error!( | ||
| error = % e, | ||
|
|
@@ -156,12 +169,18 @@ impl Coordinator { | |
| Ok((coordinator, cancellation_token)) | ||
| } | ||
|
|
||
| /// Runs the coordinator's poll loop until cancelled. | ||
| /// Runs the coordinator's polling loop until cancelled. | ||
| /// | ||
| /// Each polling iteration consists of three phases: | ||
| /// | ||
| /// 1. Schedule pending compression jobs up to the available concurrency limit. | ||
| /// 2. Wait until the next polling interval or until cancellation. | ||
| /// 3. Mark the scheduled jobs as dispatched. | ||
| /// | ||
| /// On each iteration, this method fetches the pending compression jobs, spawns a detached | ||
| /// handle to drive each one, and then sleeps until the next poll or until the cancellation | ||
| /// token is triggered. The jobs dispatched in the iteration are marked once the sleep elapses, | ||
| /// so their update does not contend with concurrent job submissions during the poll interval. | ||
| /// Jobs are marked as dispatched only after the polling interval has elapsed, giving job | ||
| /// handlers an opportunity to persist their initial Spider submission state before the | ||
| /// coordinator updates `dispatch_time`, thereby reducing contention when updating the same | ||
| /// database row. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
|
|
@@ -229,7 +248,8 @@ impl Coordinator { | |
| } | ||
| } | ||
|
|
||
| /// Fetches the pending compression jobs and spawns a detached handle to drive each one. | ||
| /// Queues new pending compression jobs and spawns as many detached handlers as the concurrency | ||
| /// limit allows. | ||
| /// | ||
| /// | ||
| /// A job whose config cannot be deserialized is marked [`CompressionJobStatus::Failed`] and | ||
|
|
@@ -239,21 +259,34 @@ impl Coordinator { | |
| /// | ||
| /// # Returns | ||
| /// | ||
| /// The IDs of the fetched jobs that were dispatched in this poll. | ||
| /// The IDs of the queued jobs that were dispatched in this poll. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if: | ||
| /// | ||
| /// * Forwards [`Self::fetch_new_job_rows`]'s return values on failure. | ||
| async fn schedule_new_jobs(&mut self) -> Result<Vec<CompressionJobId>, Error> { | ||
| let new_job_rows = self.fetch_new_job_rows().await.inspect_err(|e| { | ||
| tracing::error!(error = % e, "Failed to fetch new jobs from database."); | ||
| })?; | ||
| let dispatched_job_ids: Vec<CompressionJobId> = | ||
| new_job_rows.iter().map(|row| row.id).collect(); | ||
| for job_row in new_job_rows { | ||
| if self.pending_job_queue.is_empty() && self.job_handler_sem.available_permits() > 0 { | ||
| let new_job_rows = self.fetch_new_job_rows().await.inspect_err(|e| { | ||
| tracing::error!(error = % e, "Failed to fetch new jobs from database."); | ||
| })?; | ||
| self.pending_job_queue.extend(new_job_rows); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| let mut dispatched_job_ids = Vec::new(); | ||
| while !self.pending_job_queue.is_empty() { | ||
| let Ok(permit) = self.job_handler_sem.clone().try_acquire_owned() else { | ||
| break; | ||
| }; | ||
|
Bill-hbrhbr marked this conversation as resolved.
Outdated
|
||
|
|
||
| let job_row = self | ||
| .pending_job_queue | ||
| .pop_front() | ||
| .expect("pending job queue should not be empty"); | ||
| let job_id = job_row.id; | ||
| dispatched_job_ids.push(job_id); | ||
|
|
||
| let clp_io_config: ClpIoConfig = | ||
| match BrotliMsgpack::deserialize(&job_row.serialized_clp_io_config) { | ||
| Ok(clp_io_config) => clp_io_config, | ||
|
|
@@ -271,11 +304,14 @@ impl Coordinator { | |
| continue; | ||
| } | ||
| }; | ||
|
|
||
| tracing::info!(job_id = % job_id, "Scheduling new job."); | ||
| let Ok(job_handle) = self.create_job_handle(job_id, clp_io_config).await else { | ||
| continue; | ||
| }; | ||
|
|
||
| tokio::spawn(async move { | ||
| let _permit = permit; | ||
| let _ = job_handle.run().await.inspect_err(|e| { | ||
| tracing::error!( | ||
| error = % e, | ||
|
|
||
|
Bill-hbrhbr marked this conversation as resolved.
|
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.
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.