-
Notifications
You must be signed in to change notification settings - Fork 101
/
FDBDatabaseFactoryImpl.java
282 lines (249 loc) · 10.5 KB
/
FDBDatabaseFactoryImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
* FDBDatabaseFactory.java
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2015-2018 Apple Inc. and the FoundationDB project authors
*
* 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.apple.foundationdb.record.provider.foundationdb;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.NetworkOptions;
import com.apple.foundationdb.annotation.API;
import com.apple.foundationdb.annotation.SpotBugsSuppressWarnings;
import com.apple.foundationdb.record.RecordCoreException;
import com.apple.foundationdb.record.logging.KeyValueLogMessage;
import com.apple.foundationdb.record.logging.LogMessageKeys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.function.Supplier;
/**
* A singleton maintaining a list of {@link FDBDatabase} instances, indexed by their cluster file location.
*/
@API(API.Status.STABLE)
public class FDBDatabaseFactoryImpl extends FDBDatabaseFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(FDBDatabaseFactory.class);
@Nonnull
private static final FDBDatabaseFactoryImpl INSTANCE = new FDBDatabaseFactoryImpl();
/**
* When set to true, static options have been set on the FDB instance.
* Made volatile because multiple {@link FDBDatabaseFactory} instances can be created technically, and thus can be
* racy
* during init.
* <p>
* This option only applies (currently) to {@link FDBDatabaseFactoryImpl} and is present on the abstract class for
* backwards compatibility, it'll be moved there permanently in the next major release.
*/
protected static volatile boolean staticOptionsSet = false;
/**
* Made volatile because multiple {@link FDBDatabaseFactory} instances can be created technically, and thus can be
* racy during init.
* <p>
* Default is 1, which is basically disabled.
* <p>
* This option only applies (currently) to {@link FDBDatabaseFactoryImpl} and is present on the abstract class for
* backwards compatibility, it'll be moved there permanently in the next major release.
*/
protected static volatile int threadsPerClientVersion = 1;
@Nonnull
private FDBLocalityProvider localityProvider = FDBLocalityUtil.instance();
@Nullable
private FDB fdb;
private boolean inited;
@Nullable
private String traceDirectory = null;
@Nullable
private String traceLogGroup = null;
@Nonnull
private FDBTraceFormat traceFormat = FDBTraceFormat.DEFAULT;
@Nonnull
private APIVersion apiVersion = APIVersion.getDefault();
private boolean runLoopProfilingEnabled = false;
/**
* The default is a log-based predicate, which can also be used to enable tracing on a more granular level
* (such as by request) using {@link #setTransactionIsTracedSupplier(Supplier)}.
*/
@Nonnull
private Supplier<Boolean> transactionIsTracedSupplier = LOGGER::isTraceEnabled;
@Nonnull
public static FDBDatabaseFactoryImpl instance() {
return INSTANCE;
}
protected synchronized FDB initFDB() {
if (!inited) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info(KeyValueLogMessage.build("Staring FDB")
.addKeyAndValue(LogMessageKeys.API_VERSION, apiVersion.getVersionNumber())
.addKeyAndValue(LogMessageKeys.UNCLOSED_WARNING, unclosedWarning)
.addKeyAndValue(LogMessageKeys.TRACE_FORMAT, traceFormat)
.addKeyAndValue(LogMessageKeys.TRACE_DIRECTORY, traceDirectory)
.addKeyAndValue(LogMessageKeys.TRACE_LOG_GROUP, traceLogGroup)
.addKeyAndValue(LogMessageKeys.RUN_LOOP_PROFILING, runLoopProfilingEnabled)
.addKeyAndValue(LogMessageKeys.THREADS_PER_CLIENT_VERSION, threadsPerClientVersion)
.getMessageWithKeys());
}
fdb = FDB.selectAPIVersion(apiVersion.getVersionNumber());
fdb.setUnclosedWarning(unclosedWarning);
setStaticOptions(fdb);
NetworkOptions options = fdb.options();
if (!traceFormat.isDefaultValue()) {
options.setTraceFormat(traceFormat.getOptionValue());
}
if (traceDirectory != null) {
options.setTraceEnable(traceDirectory);
}
if (traceLogGroup != null) {
options.setTraceLogGroup(traceLogGroup);
}
if (runLoopProfilingEnabled) {
options.setEnableRunLoopProfiling();
}
if (networkExecutor == null) {
fdb.startNetwork();
} else {
fdb.startNetwork(networkExecutor);
}
inited = true;
}
return fdb;
}
private static synchronized void setStaticOptions(final FDB fdb) {
/*
* There are a few FDB settings that have to be set statically, but also need to have room
* for configuration. For the most part, FDBDatabaseFactory is a singleton and so in _theory_ this shouldn't
* matter. However, in practice it is possible to create multiple factories(i.e. in test code and such),
* and doing so may cause problems with these settings (errors thrown, that kind of thing). To avoid that,
* we have to follow a somewhat goofy pattern of making those settings static, and checking to ensure that
* we only set those options once. This block of code does that.
*
* Note that this method is synchronized on the class; this is so that multiple concurrent attempts to
* init an FDBDatabaseFactory won't cause this function to fail halfway through.
*/
if (!staticOptionsSet) {
fdb.options().setClientThreadsPerVersion(threadsPerClientVersion);
staticOptionsSet = true;
}
}
@Override
public synchronized void shutdown() {
if (inited) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(KeyValueLogMessage.of("Shutting down FDB"));
}
for (FDBDatabase database : databases.values()) {
database.close();
}
// TODO: Does this do the right thing yet?
fdb.stopNetwork();
inited = false;
}
}
@Override
@SpotBugsSuppressWarnings("IS2_INCONSISTENT_SYNC")
public void setTrace(@Nullable String traceDirectory, @Nullable String traceLogGroup) {
this.traceDirectory = traceDirectory;
this.traceLogGroup = traceLogGroup;
}
@Override
public void setTraceFormat(@Nonnull FDBTraceFormat traceFormat) {
this.traceFormat = traceFormat;
}
@Override
public synchronized void setAPIVersion(@Nonnull APIVersion apiVersion) {
if (this.apiVersion == apiVersion) {
return;
}
if (inited) {
throw new RecordCoreException("API version cannot be changed after client has already started");
}
this.apiVersion = apiVersion;
}
@Override
public synchronized APIVersion getAPIVersion() {
return apiVersion;
}
@Override
public synchronized void setRunLoopProfilingEnabled(boolean runLoopProfilingEnabled) {
if (inited) {
throw new RecordCoreException("run loop profiling can not be enabled as the client has already started");
}
this.runLoopProfilingEnabled = runLoopProfilingEnabled;
}
@Override
public boolean isRunLoopProfilingEnabled() {
return runLoopProfilingEnabled;
}
// TODO: Demote these to UNSTABLE and deprecate at some point.
@Override
public void setTransactionIsTracedSupplier(Supplier<Boolean> transactionIsTracedSupplier) {
this.transactionIsTracedSupplier = transactionIsTracedSupplier;
}
@Override
public Supplier<Boolean> getTransactionIsTracedSupplier() {
return transactionIsTracedSupplier;
}
@Override
@Nonnull
public synchronized FDBDatabase getDatabase(@Nullable String clusterFile) {
FDBDatabase database = databases.get(clusterFile);
if (database == null) {
database = new FDBDatabase(this, clusterFile);
database.setDirectoryCacheSize(getDirectoryCacheSize());
database.setTrackLastSeenVersion(getTrackLastSeenVersion());
database.setResolverStateRefreshTimeMillis(getStateRefreshTimeMillis());
database.setDatacenterId(getDatacenterId());
database.setStoreStateCache(storeStateCacheFactory.getCache(database));
databases.put(clusterFile, database);
}
return database;
}
@Override
@Nonnull
public FDBLocalityProvider getLocalityProvider() {
return localityProvider;
}
@Override
public void setLocalityProvider(@Nonnull FDBLocalityProvider localityProvider) {
this.localityProvider = localityProvider;
}
/**
* Set the number of threads per FDB client version. The default value is 1.
*
* @param threadsPerClientV the number of threads per client version. Cannot be less than 1.
*/
@SuppressWarnings("deprecation")
public static void setThreadsPerClientVersion(int threadsPerClientV) {
if (staticOptionsSet) {
throw new RecordCoreException("threads per client version cannot be changed as the version has already been initiated");
}
if (threadsPerClientV < 1) {
//if the thread count is too low, disable the setting
threadsPerClientV = 1;
}
threadsPerClientVersion = threadsPerClientV;
}
@SuppressWarnings("deprecation")
public static int getThreadsPerClientVersion() {
return threadsPerClientVersion;
}
@Nonnull
@Override
public Database open(final String clusterFile) {
FDB fdb = initFDB();
return fdb.open(clusterFile);
}
}