私はソファベースの初期化コードを静的コード ブロック内に配置しました。
static {
initCluster();
bucket = initBucket("graph");
metaBucket = initBucket("meta");
BLACKLIST = new SetObservingCache<String>(() -> getBlackList(), BLACKLIST_REFRESH_INTERVAL_SEC * 1000);
}
私はそれが良い習慣ではないことを知っていますが、このコードをマルチスレッド環境で一度だけ実行し、終了するまで他のスレッドからのすべての後続の呼び出しをブロックする必要があるため、非常に便利で目的を果たしました (ブラックリストが初期化されました)。
驚いたことに、getBlacklist() の呼び出しがタイムアウトになり、完了できませんでした。ただし、2 分後に再度呼び出すと (これが のObservingCache
動作です)、1 秒もかからずに完了しました。
これを解決するために、コードをリファクタリングし、ブラックリストの取得を遅延させました。
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()
。事前に呼び出さずに BLACKLIST から読み取ろうとする可能性があります。
クラス内の静的 (および最終ではない) フィールドは次のとおりです。
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;
}