# How to Configure Real-Time Graph Updates When Your Code Changes in Code-Graph-RAG

> Learn how to configure real-time graph updates in Code-Graph-RAG. Discover the watchdog updater for automatic knowledge graph rebuilding on code changes.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-08-20

---

**The `vitali87/code-graph-rag` repository provides a watchdog-based real-time updater that monitors code changes and automatically rebuilds the knowledge graph using configurable debounce and max-wait timers.**

The real-time update system watches your source code repository and keeps the Memgraph knowledge graph synchronized whenever files are added, modified, or deleted. Built on the [watchdog](https://pypi.org/project/watchdog/) library, it balances responsiveness with efficiency through a hybrid debounce strategy that collapses rapid successive saves into single update cycles.

## Architecture of the Real-Time Update System

Four core components work together to deliver reliable real-time synchronization:

| Component | Key Function | Source Location |
|-----------|------------|---------------|
| **`start_watcher`** | Initializes Memgraph connection, creates `GraphUpdater`, runs full scan, starts filesystem observer | [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) lines 61-73 |
| **`CodeChangeEventHandler`** | Handles filesystem events, applies debounce logic, drives the five-step update pipeline | [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) lines 62-140 |
| **`GraphUpdater`** | Manages in-memory state, parsers, and database orchestration | [`codebase_rag/graph_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_updater.py) |
| **Debouncing Constants** | Default timing values for update triggers | [`codebase_rag/constants/core.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/core.py) lines 68-71 |

According to the `vitali87/code-graph-rag` source code, the `GraphUpdater` owns three critical responsibilities: removing files from internal caches via `remove_file_from_state()`, re-parsing with `definition_processor.process_file()`, and re-running language-specific front-ends for Go, C#, and Python.

## How the Debounce Mechanism Works

The `CodeChangeEventHandler.dispatch` method (lines 47-85) implements a two-tier timing strategy:

**Debounce period** — After the first change event for a file, the system waits `debounce_seconds` (default 5s) for the filesystem to become quiet. This collapses rapid successive saves into a single update cycle.

**Max-wait ceiling** — Even if edits keep arriving, the handler forces processing after `max_wait_seconds` (default 30s) to prevent indefinite postponement.

The handler computes `remaining_wait = max_wait - time_since_first_event`, then schedules with `effective_delay = min(debounce_seconds, remaining_wait)`. If `remaining_wait` reaches zero, `_schedule_immediate_processing` triggers an instant update.

## The Five-Step Update Pipeline

When a debounced timer fires, `_process_debounced_change` → `_process_change_locked` executes this sequence:

1. **Delete old graph data** — Cypher statements `CYPHER_DELETE_MODULE` and `CYPHER_DELETE_FILE` remove previous file representations
2. **Clear in-memory caches** — `updater.remove_file_from_state()` drops parser caches, import maps, and Rust path caches
3. **Re-parse the file** — Language-specific parsers and `process_generic_file` rebuild the AST representation
4. **Re-compute CALLS edges** — All CALLS edges are erased, resolution caches reset, and `updater._process_function_calls()` rebuilds relationships
5. **Flush to Memgraph** — `ingestor.flush_all()` persists new nodes and edges

This pipeline guarantees graph consistency even during rapid edit bursts.

## Command-Line Configuration Options

Start the real-time watcher through [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) with these parameters:

```bash
python realtime_updater.py <repo_path> \
    --host <memgraph_host> \
    --port <memgraph_port> \
    --debounce <seconds> \
    --max-wait <seconds>

```

| Option | Description | Default |
|--------|-------------|---------|
| `--debounce`, `-d` | Quiet period before processing; set to `0` to disable | `5`s (`DEFAULT_DEBOUNCE_SECONDS`) |
| `--max-wait`, `-m` | Hard upper bound on wait time; must be ≥ `--debounce` | `30`s (`DEFAULT_MAX_WAIT_SECONDS`) |
| `--batch-size` | Cypher statements per Memgraph transaction | From `settings.resolve_batch_size()` |

The CLI validates that `max_wait ≥ debounce`; if violated, it logs a warning and auto-aligns values (see `main` lines 101-106).

## Practical Configuration Examples

### High-Responsiveness Mode (Demos/Development)

For near-instant feedback during active development:

```bash
python realtime_updater.py /path/to/repo --debounce 2 --max-wait 10

```

This reduces the quiet period to 2 seconds and caps total wait at 10 seconds.

### Conservative Background Mode

For CI or background monitoring with fewer graph writes:

```bash
python realtime_updater.py /path/to/repo --debounce 10 --max-wait 60

```

### Programmatic Python Configuration

Embed the watcher directly with custom timers:

```python
from realtime_updater import CodeChangeEventHandler, GraphUpdater, MemgraphIngestor
from pathlib import Path

repo_path = Path("/path/to/repo").resolve()
ingestor = MemgraphIngestor(host="localhost", port=7687)
updater = GraphUpdater(ingestor, repo_path, parsers=[], queries=[])

# Aggressive 3s debounce with 20s safety ceiling

handler = CodeChangeEventHandler(
    updater,
    debounce_seconds=3,
    max_wait_seconds=20,
)

```

### Disabling Debounce (Legacy Behavior)

Process every filesystem event instantly:

```bash
python realtime_updater.py /my/repo --debounce 0

```

With `--debounce 0`, the handler bypasses all timer logic at `dispatch` lines 42-45.

## Key Source Files Reference

| File | Purpose |
|------|---------|
| [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py) | Watchdog observer, debounce logic, CLI entry point |
| [`codebase_rag/graph_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/graph_updater.py) | Core service for parsing, state management, and updates |
| [`codebase_rag/constants/core.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/core.py) | Timing defaults and system constants |
| [`codebase_rag/tests/test_realtime_debounce.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/tests/test_realtime_debounce.py) | Debounce behavior validation suite |
| [`codebase_rag/services/graph_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/services/graph_service.py) | `MemgraphIngestor` for database writes |

## Summary

- **Real-time updates** in `code-graph-rag` use a watchdog-based watcher with configurable debouncing to balance responsiveness and efficiency.
- **Hybrid debounce strategy** combines a quiet-period wait (`--debounce`, default 5s) with a hard ceiling (`--max-wait`, default 30s).
- **Five-step pipeline** ensures atomic, consistent graph updates: delete old data, clear caches, re-parse, re-compute edges, flush to Memgraph.
- **Fine-tune via CLI** or Python API based on your latency requirements and write-frequency tolerance.

## Frequently Asked Questions

### What happens if I set `--max-wait` lower than `--debounce`?

The CLI detects this misconfiguration, emits a warning, and automatically adjusts `max_wait` to equal `debounce`. This validation runs in `main` around lines 101-106.

### Can I run the real-time watcher without debouncing entirely?

Yes. Pass `--debounce 0` to disable all timer logic. The `CodeChangeEventHandler.dispatch` method then processes each filesystem event immediately through the bypass branch at lines 42-45.

### Does the watcher handle rapid multi-file edits correctly?

The debounce mechanism operates per-file, so simultaneous edits to different files trigger independent update cycles. Each file's changes are collapsed based on its own event timing, preventing redundant re-parsing.

### Where are the default timing values defined?

`DEFAULT_DEBOUNCE_SECONDS` (5) and `DEFAULT_MAX_WAIT_SECONDS` (30) are constants in [`codebase_rag/constants/core.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/core.py) lines 68-71, imported by [`realtime_updater.py`](https://github.com/vitali87/code-graph-rag/blob/main/realtime_updater.py).