Skip to content

Commit 410c2c5

Browse files
authored
feat(combine-update): combine update calls whenever safe (#729)
1 parent 725e022 commit 410c2c5

File tree

3 files changed

+60
-3
lines changed

3 files changed

+60
-3
lines changed

docs/docs/core/flow_methods.mdx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ cocoindex update --setup main.py
157157

158158
The `Flow.update()` method creates/updates data in the target.
159159

160-
Once the function returns, the target data is fresh up to the moment when the function is called.
161160

162161
```python
163162
stats = demo_flow.update()
@@ -171,6 +170,16 @@ stats = await demo_flow.update_async()
171170
print(stats)
172171
```
173172

173+
Once the function finishes, the target data is fresh up to the moment when the function is called.
174+
175+
:::info
176+
177+
`update()` and `update_async()` can be called simultaneously, even if a previous call is not finished yet.
178+
It's quite cheap to do so, as CocoIndex will automatically combine multiple calls into a single update, as long as we hold the guarantee that the target data is fresh up to the moment when the last call is initiated.
179+
180+
:::
181+
182+
174183
</TabItem>
175184
</Tabs>
176185

src/execution/source_indexer.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::prelude::*;
1+
use crate::{
2+
prelude::*,
3+
service::error::{SharedError, SharedResult, SharedResultExt},
4+
};
25

36
use futures::future::Ready;
47
use sqlx::PgPool;
@@ -33,9 +36,12 @@ struct SourceIndexingState {
3336
rows: HashMap<value::KeyValue, SourceRowIndexingState>,
3437
scan_generation: usize,
3538
}
39+
3640
pub struct SourceIndexingContext {
3741
flow: Arc<builder::AnalyzedFlow>,
3842
source_idx: usize,
43+
pending_update: Mutex<Option<Shared<BoxFuture<'static, SharedResult<()>>>>>,
44+
update_sem: Semaphore,
3945
state: Mutex<SourceIndexingState>,
4046
setup_execution_ctx: Arc<exec_ctx::FlowSetupExecutionContext>,
4147
}
@@ -88,6 +94,8 @@ impl SourceIndexingContext {
8894
rows,
8995
scan_generation,
9096
}),
97+
pending_update: Mutex::new(None),
98+
update_sem: Semaphore::new(1),
9199
setup_execution_ctx,
92100
})
93101
}
@@ -269,11 +277,49 @@ impl SourceIndexingContext {
269277
pool.clone(),
270278
))
271279
}
272-
273280
pub async fn update(
274281
self: &Arc<Self>,
275282
pool: &PgPool,
276283
update_stats: &Arc<stats::UpdateStats>,
284+
) -> Result<()> {
285+
let pending_update_fut = {
286+
let mut pending_update = self.pending_update.lock().unwrap();
287+
if let Some(pending_update_fut) = &*pending_update {
288+
pending_update_fut.clone()
289+
} else {
290+
let slf = self.clone();
291+
let pool = pool.clone();
292+
let update_stats = update_stats.clone();
293+
let task = tokio::spawn(async move {
294+
{
295+
let _permit = slf.update_sem.acquire().await?;
296+
{
297+
let mut pending_update = slf.pending_update.lock().unwrap();
298+
*pending_update = None;
299+
}
300+
slf.update_once(&pool, &update_stats).await?;
301+
}
302+
anyhow::Ok(())
303+
});
304+
let pending_update_fut = async move {
305+
task.await
306+
.map_err(SharedError::from)?
307+
.map_err(SharedError::new)
308+
}
309+
.boxed()
310+
.shared();
311+
*pending_update = Some(pending_update_fut.clone());
312+
pending_update_fut
313+
}
314+
};
315+
pending_update_fut.await.std_result()?;
316+
Ok(())
317+
}
318+
319+
async fn update_once(
320+
self: &Arc<Self>,
321+
pool: &PgPool,
322+
update_stats: &Arc<stats::UpdateStats>,
277323
) -> Result<()> {
278324
let plan = self.flow.get_execution_plan().await?;
279325
let import_op = &plan.import_ops[self.source_idx];

src/service/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ pub fn shared_ok<T>(value: T) -> Result<T, SharedError> {
117117
Ok(value)
118118
}
119119

120+
pub type SharedResult<T> = Result<T, SharedError>;
121+
120122
pub struct SharedErrorWrapper(SharedError);
121123

122124
impl Display for SharedErrorWrapper {

0 commit comments

Comments
 (0)