Я поместил свой код инициализации couchbase в блок статического кода:
static {
initCluster();
bucket = initBucket("graph");
metaBucket = initBucket("meta");
BLACKLIST = new SetObservingCache<String>(() -> getBlackList(), BLACKLIST_REFRESH_INTERVAL_SEC * 1000);
}
Я знаю, что это не очень хорошая практика, но это было очень удобно и служило своей цели, так как мне нужно, чтобы этот код запускался ровно один раз в многопоточной среде и блокировал все последующие вызовы из других потоков до его завершения (черный список был инициализирован).
К моему удивлению, время ожидания вызова getBlacklist () истекло, и его не удалось выполнить. Однако при повторном вызове через 2 минуты (это то, что делает ObservingCache
), он завершился менее чем за секунду.
Чтобы решить эту проблему, я реорганизовал свой код и отложил получение черного списка:
public boolean isBlacklisted(String key) {
// BLACKLIST variable should NEVER be touched outside of this context.
assureBlacklistIsPopulated();
return BLACKLIST != null ? BLACKLIST.getItems().contains(key) : false;
}
private void assureBlacklistIsPopulated() {
if (!ENABLE_BLACKLIST) {
return;
}
if (BLACKLIST == null) {
synchronized (CouchConnectionManager.class) {
if (BLACKLIST == null) {
BLACKLIST = new SetObservingCache<String>(() -> getBlackList(), BLACKLIST_REFRESH_INTERVAL_SEC * 1000);
}
}
}
}
Вызов isBlacklisted()
блокирует все другие потоки, которые пытаются проверить, внесена ли запись в черный список, пока черный список не будет инициализирован. Я не большой поклонник этого решения, потому что оно очень подробное и подвержено ошибкам - можно попытаться прочитать из ЧЕРНОГО СПИСКА, не вызывая заранее assureBlacklistIsPopulated()
.
Статические (и не окончательные) поля в классе следующие:
private static CouchbaseCluster cluster;
private static Bucket bucket;
private static Bucket metaBucket;
private static SetObservingCache<String> BLACKLIST;
Я не могу понять, почему вызов завершился успешно, если он не был частью статического блока инициализации. Есть ли какая-либо известная уязвимость статического блока инициализации, связанная с производительностью, о которой я не знаю?
РЕДАКТИРОВАТЬ: добавлен код инициализации для каждого запроса.
private Bucket initBucket(String bucketName) {
while(true) {
Throwable t = null;
try {
ReportableThread.updateStatus("Initializing bucket " + bucketName);
return cluster.openBucket(bucketName);
} catch(Throwable t1) {
t1.printStackTrace();
t = t1;
}
try {
ReportableThread.updateStatus(String.format("Failed to open bucket: %s reason: %s", bucketName, t));
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void initCluster() {
CouchbaseEnvironment env = DefaultCouchbaseEnvironment
.builder()
.kvTimeout(MINUTE)
.connectTimeout(MINUTE)
.retryStrategy(FailFastRetryStrategy.INSTANCE)
.requestBufferSize(16384 * 2)
.responseBufferSize(16384 * 2)
.build();
while(true) {
ReportableThread.updateStatus("Initializing couchbase cluster");
Throwable t = null;
try {
cluster = CouchbaseCluster.create(env, getServerNodes());
if(cluster != null) {
return;
}
} catch(Throwable t1) {
t1.printStackTrace();
t = t1;
}
try {
ReportableThread.updateStatus(String.format("Failed to create connection to couch %s", t));
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public Set<String> getBlackList() {
ReportableThread.updateStatus("Getting black list");
AbstractDocument<?> abstractDoc = get("blacklist", metaBucket, JsonArrayDocument.class);
JsonArrayDocument doc = null;
if (abstractDoc != null && abstractDoc instanceof JsonArrayDocument) {
doc = (JsonArrayDocument)abstractDoc;
} else {
return new HashSet<String>();
}
ReportableThread.updateStatus(String.format("%s: Got %d items | sorting items", new Date(System.currentTimeMillis()).toString(), doc.content().size()));
HashSet<String> ret = new HashSet<String>();
for (Object string : doc.content()) {
if (string != null) {
ret.add(string.toString());
}
}
return ret;
}
initCluster
,initBucket
иgetBlackList()
? - person Simon Baslé   schedule 30.11.2015