MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / sync

Method sync

src/index.ts:777–1035  ·  view source on GitHub ↗

* Sync with current file state (incremental update) * * Uses a mutex to prevent concurrent indexing operations.

(options: IndexOptions = {})

Source from the content-addressed store, hash-verified

775 * Uses a mutex to prevent concurrent indexing operations.
776 */
777 async sync(options: IndexOptions = {}): Promise<SyncResult> {
778 return this.indexMutex.withLock(async () => {
779 try {
780 this.fileLock.acquire();
781 } catch {
782 return { filesChecked: 0, filesAdded: 0, filesModified: 0, filesRemoved: 0, nodesUpdated: 0, durationMs: 0 };
783 }
784 // Defer WAL auto-checkpointing for the whole incremental run, exactly
785 // as indexAll does for the bulk path (#1231): sync's store loop and its
786 // resolution passes churn the same FTS + secondary-index hot pages, and
787 // at the default 1000-page cadence the inline checkpoints re-write them
788 // over and over — on HDD-class storage a 7-file sync took 2 minutes at
789 // 0-2% CPU (#1248). The cost scales with the EXISTING database size,
790 // not the change size, so small syncs on big indexes hurt most. The
791 // valve bounds WAL growth off-thread; runMaintenance at the end does
792 // the final fold-up before the interval is restored in the finally.
793 // Same kill switch as indexAll: CODEGRAPH_NO_WAL_DEFER=1. Idle valve
794 // cost is one timer, so watcher-frequency syncs stay cheap.
795 const deferWal = process.env.CODEGRAPH_NO_WAL_DEFER !== '1' && this.db.getJournalMode() === 'wal';
796 let walValve: WalCheckpointValve | null = null;
797 let priorAutocheckpoint = 1000;
798 if (deferWal) {
799 priorAutocheckpoint = this.db.getWalAutocheckpoint();
800 this.db.setWalAutocheckpoint(0);
801 walValve = new WalCheckpointValve(
802 this.db,
803 resolveWalValveMb(process.env.CODEGRAPH_WAL_VALVE_MB, this.db.getDbFileSizeBytes()),
804 undefined,
805 options.verbose ? (m) => console.log(`[wal-valve] ${m}`) : undefined
806 );
807 walValve.start();
808 }
809 try {
810 // Captured BEFORE the sync runs: the sync's own incremental writes
811 // populate vocab rows for the files it touches, so an end-of-sync
812 // emptiness check would see "non-empty" and skip the backfill forever,
813 // leaving every unchanged file's names unsegmented.
814 const vocabWasEmpty = (() => {
815 try { return this.queries.isNameSegmentVocabEmpty(); } catch { return false; }
816 })();
817
818 const result = await this.orchestrator.sync(options.onProgress, options.paths);
819
820 // Fold the store phase's WAL BEFORE the post-store reads below
821 // (resolution reads on the main thread) — same rationale as
822 // indexAll's fold between store and resolution.
823 if (walValve) await walValve.foldNow();
824
825 // Cross-file finalization (e.g. NestJS RouterModule prefixes). Run on
826 // every sync that touched files so edits to `app.module.ts` propagate
827 // to controllers in unchanged files. The pass is idempotent and cheap
828 // (regex over *.module.ts only).
829 if (result.filesAdded > 0 || result.filesModified > 0) {
830 this.resolver.runPostExtract();
831 } else if (result.filesRemoved > 0) {
832 // A pure-removal sync still resolves refs below — the deletion path
833 // resurrects the removed file's incoming edges as pending refs
834 // (#1240 removal case) and the orphan sweep consumes them. In a

Calls 15

getIndexStateMethod · 0.95
resolveWalValveMbFunction · 0.90
getWalAutocheckpointMethod · 0.80
setWalAutocheckpointMethod · 0.80
getDbFileSizeBytesMethod · 0.80
foldNowMethod · 0.80
runPostExtractMethod · 0.80
clearCachesMethod · 0.80

Tested by

no test coverage detected