Skip to content

Commit 34c48fc

Browse files
committed
feat(core): implement lazy sync for MetadataStore cache
- Remove eager SyncMetadataStoreFromConfigs on notebook open - Add vxcore_notebook_rebuild_cache API for explicit cache rebuild - Implement lazy sync in GetFolderConfig - folders synced on access - Add SyncFolderToStore and GetParentFolderId helpers - Add test for rebuild cache functionality
1 parent 17e41af commit 34c48fc

File tree

11 files changed

+184
-12
lines changed

11 files changed

+184
-12
lines changed

include/vxcore/vxcore.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ VXCORE_API VxCoreError vxcore_notebook_update_config(VxCoreContextHandle context
5252
const char *notebook_id,
5353
const char *config_json);
5454

55+
// Rebuilds the metadata cache for the notebook from ground truth (vx.json files).
56+
// Use this when the cache seems out of sync or corrupted.
57+
// The cache uses lazy sync by default - this forces a full rebuild.
58+
VXCORE_API VxCoreError vxcore_notebook_rebuild_cache(VxCoreContextHandle context,
59+
const char *notebook_id);
60+
5561
VXCORE_API VxCoreError vxcore_folder_create(VxCoreContextHandle context, const char *notebook_id,
5662
const char *parent_path, const char *folder_name,
5763
char **out_folder_id);

src/api/vxcore_notebook_api.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,29 @@ VXCORE_API VxCoreError vxcore_notebook_update_config(VxCoreContextHandle context
173173
return VXCORE_ERR_UNKNOWN;
174174
}
175175
}
176+
177+
VXCORE_API VxCoreError vxcore_notebook_rebuild_cache(VxCoreContextHandle context,
178+
const char *notebook_id) {
179+
if (!context || !notebook_id) {
180+
return VXCORE_ERR_NULL_POINTER;
181+
}
182+
183+
auto *ctx = reinterpret_cast<vxcore::VxCoreContext *>(context);
184+
185+
try {
186+
auto *notebook = ctx->notebook_manager->GetNotebook(notebook_id);
187+
if (!notebook) {
188+
ctx->last_error = "Notebook not found";
189+
return VXCORE_ERR_NOT_FOUND;
190+
}
191+
192+
VxCoreError err = notebook->RebuildCache();
193+
if (err != VXCORE_OK) {
194+
ctx->last_error = "Failed to rebuild notebook cache";
195+
}
196+
return err;
197+
} catch (...) {
198+
ctx->last_error = "Unknown error rebuilding notebook cache";
199+
return VXCORE_ERR_UNKNOWN;
200+
}
201+
}

src/core/bundled_folder_manager.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ VxCoreError BundledFolderManager::GetFolderConfig(const std::string &folder_path
204204
return error;
205205
}
206206

207+
// Lazy sync: populate MetadataStore with this folder's data
208+
std::string parent_id = GetParentFolderId(folder_path);
209+
SyncFolderToStore(folder_path, *config, parent_id);
210+
207211
*out_config = config.get();
208212
config_cache_[folder_path] = std::move(config);
209213
return VXCORE_OK;
@@ -1391,4 +1395,69 @@ VxCoreError BundledFolderManager::SyncMetadataStoreFromConfigs() {
13911395
}
13921396
}
13931397

1398+
std::string BundledFolderManager::GetParentFolderId(const std::string &folder_path) {
1399+
if (folder_path.empty() || folder_path == ".") {
1400+
return ""; // Root folder has no parent
1401+
}
1402+
1403+
const auto [parent_path, folder_name] = SplitPath(folder_path);
1404+
(void)folder_name; // Unused
1405+
1406+
FolderConfig *parent_config = GetCachedConfig(parent_path);
1407+
if (parent_config) {
1408+
return parent_config->id;
1409+
}
1410+
1411+
// Parent not in cache - try to load it (which will also trigger lazy sync for parent)
1412+
VxCoreError error = GetFolderConfig(parent_path, &parent_config);
1413+
if (error == VXCORE_OK && parent_config) {
1414+
return parent_config->id;
1415+
}
1416+
1417+
return ""; // Parent not found
1418+
}
1419+
1420+
void BundledFolderManager::SyncFolderToStore(const std::string &folder_path,
1421+
const FolderConfig &config,
1422+
const std::string &parent_folder_id) {
1423+
auto *store = notebook_->GetMetadataStore();
1424+
if (!store) {
1425+
return; // No store available
1426+
}
1427+
1428+
// Check if folder already exists in store
1429+
auto existing_folder = store->GetFolder(config.id);
1430+
if (existing_folder.has_value()) {
1431+
// Folder exists - check if we need to update sync state
1432+
// For now, we assume the store is up-to-date if folder exists
1433+
// (write-through cache should keep it in sync)
1434+
VXCORE_LOG_DEBUG("SyncFolderToStore: Folder already in store: id=%s, path=%s",
1435+
config.id.c_str(), folder_path.c_str());
1436+
return;
1437+
}
1438+
1439+
VXCORE_LOG_INFO("SyncFolderToStore: Adding folder to store: id=%s, path=%s", config.id.c_str(),
1440+
folder_path.c_str());
1441+
1442+
// Create folder record in store
1443+
StoreFolderRecord folder_record = ToStoreFolderRecord(config, parent_folder_id);
1444+
if (!store->CreateFolder(folder_record)) {
1445+
VXCORE_LOG_WARN("SyncFolderToStore: Failed to create folder in store: id=%s",
1446+
config.id.c_str());
1447+
return;
1448+
}
1449+
1450+
// Create file records in store
1451+
for (const auto &file : config.files) {
1452+
StoreFileRecord file_record = ToStoreFileRecord(file, config.id);
1453+
if (!store->CreateFile(file_record)) {
1454+
VXCORE_LOG_WARN("SyncFolderToStore: Failed to create file in store: id=%s", file.id.c_str());
1455+
// Continue anyway - best effort
1456+
}
1457+
}
1458+
1459+
VXCORE_LOG_DEBUG("SyncFolderToStore: Synced folder with %zu files: path=%s", config.files.size(),
1460+
folder_path.c_str());
1461+
}
1462+
13941463
} // namespace vxcore

src/core/bundled_folder_manager.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ class BundledFolderManager : public FolderManager {
100100

101101
FileRecord *FindFileRecord(FolderConfig &config, const std::string &file_name);
102102

103+
// Sync a single folder's data to MetadataStore after loading from vx.json
104+
// This is the lazy sync implementation - called when a folder is accessed
105+
void SyncFolderToStore(const std::string &folder_path, const FolderConfig &config,
106+
const std::string &parent_folder_id);
107+
108+
// Get the parent folder's ID (UUID) for a given folder path
109+
std::string GetParentFolderId(const std::string &folder_path);
110+
103111
std::map<std::string, std::unique_ptr<FolderConfig>> config_cache_;
104112
};
105113

src/core/bundled_notebook.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,9 @@ VxCoreError BundledNotebook::Open(const std::string &local_data_folder,
104104
return err;
105105
}
106106

107-
// Sync MetadataStore from config files (vx.json)
108-
// This rebuilds the cache from ground truth on every open
109-
auto *bundled_folder_manager =
110-
static_cast<BundledFolderManager *>(notebook->folder_manager_.get());
111-
err = bundled_folder_manager->SyncMetadataStoreFromConfigs();
112-
if (err != VXCORE_OK) {
113-
VXCORE_LOG_ERROR("Failed to sync MetadataStore from configs: root=%s, error=%d",
114-
root_folder.c_str(), err);
115-
return err;
116-
}
107+
// Note: We do NOT sync MetadataStore from config files here.
108+
// The cache uses lazy sync - data is loaded on demand when accessed.
109+
// Users can call RebuildCache() if they need a full refresh.
117110

118111
out_notebook = std::move(notebook);
119112
return VXCORE_OK;
@@ -170,4 +163,13 @@ VxCoreError BundledNotebook::UpdateConfig(const NotebookConfig &config) {
170163
}
171164
}
172165

166+
VxCoreError BundledNotebook::RebuildCache() {
167+
auto *bundled_folder_manager = dynamic_cast<BundledFolderManager *>(folder_manager_.get());
168+
if (!bundled_folder_manager) {
169+
VXCORE_LOG_ERROR("RebuildCache: folder_manager is not BundledFolderManager");
170+
return VXCORE_ERR_INVALID_STATE;
171+
}
172+
return bundled_folder_manager->SyncMetadataStoreFromConfigs();
173+
}
174+
173175
} // namespace vxcore

src/core/bundled_notebook.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class BundledNotebook : public Notebook {
1717
std::unique_ptr<Notebook> &out_notebook);
1818

1919
VxCoreError UpdateConfig(const NotebookConfig &config) override;
20+
VxCoreError RebuildCache() override;
2021

2122
private:
2223
BundledNotebook(const std::string &local_data_folder, const std::string &root_folder);

src/core/notebook.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class Notebook {
7878
VxCoreError GetTags(std::string &out_tags_json) const;
7979
TagNode *FindTag(const std::string &tag_name);
8080

81+
// Rebuild the metadata cache from ground truth (config files).
82+
// Returns VXCORE_OK on success.
83+
virtual VxCoreError RebuildCache() = 0;
84+
8185
// Clean and get path related to notebook root folder.
8286
// Returns null string if |path| is not under notebook root folder.
8387
std::string GetCleanRelativePath(const std::string &path) const;

src/core/raw_notebook.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,10 @@ VxCoreError RawNotebook::UpdateConfig(const NotebookConfig &config) {
133133
}
134134
}
135135

136+
VxCoreError RawNotebook::RebuildCache() {
137+
// Raw notebooks don't have vx.json config files to sync from.
138+
// Metadata is stored only in the database.
139+
return VXCORE_OK;
140+
}
141+
136142
} // namespace vxcore

src/core/raw_notebook.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class RawNotebook : public Notebook {
1515
const std::string &id, std::unique_ptr<Notebook> &out_notebook);
1616

1717
VxCoreError UpdateConfig(const NotebookConfig &config) override;
18+
VxCoreError RebuildCache() override;
1819

1920
private:
2021
RawNotebook(const std::string &local_data_folder, const std::string &root_folder);

tests/test_folder.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,7 +1285,9 @@ int test_folder_create_path_trailing_slash() {
12851285
// MetadataStore Sync Integration Tests
12861286
// ============================================================================
12871287

1288-
// Test that metadata store syncs correctly when notebook is reopened
1288+
// Test that metadata store syncs correctly when notebook is reopened.
1289+
// Note: With lazy sync architecture, data is synced to MetadataStore on-demand
1290+
// when folders/files are accessed, not eagerly on notebook open.
12891291
int test_metadata_store_sync_on_reopen() {
12901292
std::cout << " Running test_metadata_store_sync_on_reopen..." << std::endl;
12911293
cleanup_test_dir("test_ms_sync_reopen_nb");
@@ -1340,7 +1342,7 @@ int test_metadata_store_sync_on_reopen() {
13401342
vxcore_string_free(notebook_id);
13411343
notebook_id = nullptr;
13421344

1343-
// Reopen notebook - this should trigger SyncMetadataStoreFromConfigs
1345+
// Reopen notebook - lazy sync will populate MetadataStore when folders are accessed
13441346
err = vxcore_notebook_open(ctx, "test_ms_sync_reopen_nb", &notebook_id);
13451347
ASSERT_EQ(err, VXCORE_OK);
13461348
ASSERT_NOT_NULL(notebook_id);

0 commit comments

Comments
 (0)