Details
Description
Issue Summary
If the tags.cache file for a repository becomes corrupt, some actions on refs for that repository (e.g. creating a new tag via REST, or viewing tags for a commit in the UI) fail.
Steps to Reproduce
- Corrupt the tags.cache file which is located at <BITBUCKET_SHARED_HOME>/data/repositories/<REPOSITORY_ID>/app-info/tags.cache. It's unclear currently exactly how the file can become corrupted from normal application use, but to reproduce the resultant symptoms it's possible to for example edit the file trim the final line to only be a few characters long.
- Perform an action related to refs in that repository (e.g. attempt to create a new tag via REST, or view tags for a commit in the UI).
Expected Results
The system is able to recognise the corrupt tags.cache file and regenerate it automatically, and any ref actions are able to be performed successfully.
Actual Results
The below exception (or similar, coming from other ref related actions) is thrown in the atlassian-bitbucket.log file when attempting to create a tag:
java.lang.StringIndexOutOfBoundsException: String index out of range: 40 at java.lang.String.substring(String.java:1963) at com.atlassian.stash.internal.scm.git.ref.DefaultTagPeeler.lambda$resolveFromCache$0(DefaultTagPeeler.java:175) at java.util.Iterator.forEachRemaining(Iterator.java:116) at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801) at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647) at com.atlassian.stash.internal.scm.git.ref.DefaultTagPeeler.resolveFromCache(DefaultTagPeeler.java:175) at com.atlassian.stash.internal.scm.git.ref.DefaultTagPeeler.internalPeel(DefaultTagPeeler.java:157) at com.atlassian.stash.internal.scm.git.ref.DefaultTagPeeler.peel(DefaultTagPeeler.java:112) at com.atlassian.stash.internal.scm.git.ref.LooseRefDb.buildTag(LooseRefDb.java:182) at com.atlassian.stash.internal.scm.git.ref.LooseRefDb.lambda$readRef$16(LooseRefDb.java:415) at com.atlassian.stash.internal.scm.git.ref.LooseRefDb.retryingRead(LooseRefDb.java:149) at com.atlassian.stash.internal.scm.git.ref.LooseRefDb.readRef(LooseRefDb.java:406) at com.atlassian.stash.internal.scm.git.ref.LooseRefDb.lambda$new$2(LooseRefDb.java:55) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at com.atlassian.stash.internal.scm.git.ref.FileTree$FileTreeSpliterator.tryAdvance(FileTree.java:114) at java.util.stream.StreamSpliterators$WrappingSpliterator.lambda$initPartialTraversalState$0(StreamSpliterators.java:295) at java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.fillBuffer(StreamSpliterators.java:207) at java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.doAdvance(StreamSpliterators.java:162) at java.util.stream.StreamSpliterators$WrappingSpliterator.tryAdvance(StreamSpliterators.java:301) at com.atlassian.stash.internal.scm.git.ref.CompositeGitRefDb$DedupingSpliterator.advance(CompositeGitRefDb.java:241) at com.atlassian.stash.internal.scm.git.ref.CompositeGitRefDb$DedupingSpliterator.<init>(CompositeGitRefDb.java:163) at com.atlassian.stash.internal.scm.git.ref.CompositeGitRefDb.stream(CompositeGitRefDb.java:143) at com.atlassian.stash.internal.scm.git.ref.CompositeGitRefDb.stream(CompositeGitRefDb.java:139) at com.atlassian.stash.internal.scm.git.command.refdb.TagsGitRefDbCommand.internalCall(TagsGitRefDbCommand.java:42) at com.atlassian.stash.internal.scm.git.command.refdb.TagsGitRefDbCommand.internalCall(TagsGitRefDbCommand.java:22) at com.atlassian.stash.internal.scm.git.command.refdb.AbstractGitRefDbCommand.call(AbstractGitRefDbCommand.java:51) at com.atlassian.stash.internal.repository.DefaultRefService.streamTags(DefaultRefService.java:371) at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26) at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56) at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60) at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70) at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53) at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57) at com.atlassian.bitbucket.internal.tag.idx.TagIndexer$1.apply(TagIndexer.java:134) at com.atlassian.util.contentcache.internal.AbstractCacheEntry$BaseAccess.streamToCache(AbstractCacheEntry.java:362) at com.atlassian.util.contentcache.internal.AbstractCacheEntry$CacheEntryAccess.stream(AbstractCacheEntry.java:452) at com.atlassian.util.contentcache.internal.AbstractContentCache.stream(AbstractContentCache.java:182) at com.atlassian.bitbucket.internal.tag.idx.TagIndexer.readIndex(TagIndexer.java:123) at com.atlassian.bitbucket.internal.tag.idx.TagIndexer.lambda$null$0(TagIndexer.java:171) at com.atlassian.stash.internal.user.DefaultEscalatedSecurityContext.call(DefaultEscalatedSecurityContext.java:59) at com.atlassian.bitbucket.internal.tag.idx.TagIndexer.lambda$scheduleReindex$1(TagIndexer.java:166) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.lang.Thread.run(Thread.java:750) ... 56 frames trimmed
Workaround
- Forcing the tag file to be recreated (e.g. creating and deleting a test tag, outright deleting the file, pushes which happen to trigger a repack) will fix the issue. This has a side effect that repositories with frequent activity may not experience this issue as the cache will be recreated quick enough, bur repositories with more infrequent activity may experience it more.