From a2ec1248fcbc425f168caa66504b2706f66fb41d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 26 Feb 2026 17:30:42 +0200 Subject: [PATCH] Fix per-key commit caching in DefaultGitSyncService The lastCommit field was shared across all repository keys, causing MissingObjectException when multiple repositories were registered. When onUpdate fired for repo A it overwrote lastCommit, and subsequent listFiles/getFileContent calls for repo B used repo A's commit whose tree objects don't exist in repo B's object database. Changed to a per-key Map so each repository's resolved commit is stored and retrieved independently. Co-Authored-By: Claude Opus 4.6 --- .../service/sync/DefaultGitSyncService.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java index 3f405e145b..4ffb0940b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java @@ -48,7 +48,7 @@ public class DefaultGitSyncService implements GitSyncService { private final Map repositories = new ConcurrentHashMap<>(); private final Map updateListeners = new ConcurrentHashMap<>(); - private RevCommit lastCommit; + private final Map lastCommits = new ConcurrentHashMap<>(); @Override public void registerSync(String key, String repoUri, String branch, long fetchFrequencyMs, Runnable onUpdate) { @@ -87,7 +87,7 @@ public class DefaultGitSyncService implements GitSyncService { @Override public List listFiles(String key, String path, int depth, FileType type) { GitRepository repository = getRepository(key); - return repository.listFilesAtCommit(lastCommit, path, depth).stream() + return repository.listFilesAtCommit(getLastCommit(key), path, depth).stream() .filter(file -> type == null || file.type() == type) .toList(); } @@ -96,7 +96,7 @@ public class DefaultGitSyncService implements GitSyncService { @Override public byte[] getFileContent(String key, String path) { GitRepository repository = getRepository(key); - return repository.getFileContentAtCommit(path, lastCommit); + return repository.getFileContentAtCommit(path, getLastCommit(key)); } @Override @@ -143,7 +143,7 @@ public class DefaultGitSyncService implements GitSyncService { GitRepository repository = getRepository(key); String branchRef = getBranchRef(repository); try { - lastCommit = repository.resolveCommit(branchRef); + lastCommits.put(key, repository.resolveCommit(branchRef)); } catch (Throwable e) { log.error("[{}] Failed to resolve commit for ref {}", key, branchRef, e); return; @@ -166,6 +166,14 @@ public class DefaultGitSyncService implements GitSyncService { return Path.of(repositoriesFolder, name); } + private RevCommit getLastCommit(String key) { + RevCommit commit = lastCommits.get(key); + if (commit == null) { + throw new IllegalStateException(key + " repository has no resolved commit"); + } + return commit; + } + private String getBranchRef(GitRepository repository) { return "refs/remotes/origin/" + repository.getSettings().getDefaultBranch(); }