blob: b92c79055bced20cd33b76b07434b540e3806d99 [file] [log] [blame]
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.googlesource.gerrit.plugins.findowners;
import java.util.Collection;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import org.eclipse.jgit.lib.Repository;
/** Save OwnersDb in a cache for multiple calls to submit_filter. */
class Cache {
// The OwnersDb is created from OWNERS files in directories that
// contain changed files of a patch set, which belongs to a project
// and branch. OwnersDb can be cached if the head of a project branch
// and the patch set are not changed.
// Although the head of a project branch could be changed by other users,
// it is better to assume the same for a patch set during a short period
// of time. So multiple checks would have the same result. For example,
// one client UI action can trigger multiple HTTP requests.
// Each HTTP request has one StoredValues,
// and can trigger multiple Prolog submit_filter.
// Each submit_filter has one Prolog engine.
// It would not be enough to keep the cache in a Prolog engine environment
// or a StoredValues.
// We keep the cache in a Java class static object for all HTTP requests.
// OwnersDb is cached for up to 10 seconds.
private static final int CACHE_LIFE_MSEC = 10000;
// When branch is "refs/heads/xyz" use only "xyz",
// to share cached OwnersDb between these two branch names.
private static final String REFS_HEADS = "refs/heads/";
static class CachedObj {
long time; // system time in milliseconds, when db is created
String key; // (changeId, patchSetId, branchName)
OwnersDb db;
CachedObj(String k, OwnersDb x) {
time = new Date().getTime();
key = k;
db = x;
}
}
// Before a new CachedObj is added to the tail of dbQueue,
// old and obsolete CachedObj are removed from the head.
private static final Deque<CachedObj> dbQueue = new LinkedList<CachedObj>();
// A HashMap provides quick lookup with a key.
private static final HashMap<String, CachedObj> dbCache =
new HashMap<String, CachedObj>();
private static long minCachedObjectTime() {
// Cached objects must be used within CACHE_LIFE_MSEC.
return new Date().getTime() - CACHE_LIFE_MSEC;
}
static String makeKey(int change, int patch, String branch) {
if (branch.indexOf(REFS_HEADS) == 0) {
branch = branch.substring(REFS_HEADS.length());
}
return change + ":" + patch + ":" + branch;
}
private static void saveCachedDb(String key, OwnersDb db) {
CachedObj obj = new CachedObj(key, db);
long minTime = minCachedObjectTime();
synchronized (dbCache) {
// Remove cached objects older than minTime.
while (dbQueue.size() > 0 && dbQueue.peek().time < minTime) {
dbCache.remove(dbQueue.peek().key);
dbQueue.removeFirst();
}
// Add the new one to the tail.
dbCache.put(key, obj);
dbQueue.addLast(obj);
}
}
static OwnersDb get(Server server, String key, String url, String project,
String branch, Collection<String> files) {
return get(server, key, null, url, project, branch, files);
}
static OwnersDb get(Server server, String key, Repository repository,
String branch, Collection<String> files) {
return get(server, key, repository, null, null, branch, files);
}
private static OwnersDb get(
Server server, String key, Repository repository, String url,
String project, String branch, Collection<String> files) {
OwnersDb db = null;
long minTime = minCachedObjectTime();
synchronized (dbCache) {
if (dbCache.containsKey(key)) {
CachedObj obj = dbCache.get(key);
if (obj.time >= minTime) {
db = obj.db;
}
}
}
if (null == db) {
db = (null != repository)
? new OwnersDb(server, key, repository, branch, files)
: new OwnersDb(server, key, url, project, branch, files);
saveCachedDb(key, db);
}
return db;
}
}