Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / DatastoreServiceConfig.java
blob6d699a60be512050d18e4189ff25c23b45750f1d
1 // Copyright 2010 Google Inc. All Rights Reserved.
2 package com.google.appengine.api.datastore;
4 import static com.google.common.base.Preconditions.checkNotNull;
6 import com.google.appengine.api.datastore.ReadPolicy.Consistency;
7 import com.google.apphosting.api.ApiProxy;
8 import com.google.common.base.Preconditions;
10 import java.io.ByteArrayInputStream;
11 import java.io.InputStream;
13 /**
14 * User-configurable properties of the datastore.
16 * Notes on usage:<br>
17 * The recommended way to instantiate a {@code DatastoreServiceConfig} object
18 * is to statically import {@link Builder}.* and invoke a static creation
19 * method followed by an instance mutator (if needed):
21 * <pre>
22 * import static com.google.appengine.api.datastore.DatastoreServiceConfig.Builder.*;
23 * import com.google.appengine.api.datastore.ReadPolicy.Consistency;
25 * ...
27 * // eventually consistent reads
28 * DatastoreServiceConfig config = withReadPolicy(new ReadPolicy(Consistency.EVENTUAL));
30 * // eventually consistent reads with a 5 second deadline
31 * DatastoreServiceConfig config =
32 * withReadPolicy(new ReadPolicy(Consistency.EVENTUAL)).deadline(5.0);
33 * </pre>
36 public final class DatastoreServiceConfig {
38 /**
39 * Datastore service API version.
41 enum ApiVersion {
42 V3(false),
43 CLOUD_DATASTORE_V1_VIA_API_PROXY(false),
44 CLOUD_DATASTORE_V1_REMOTE(true);
46 private final boolean isCloudService;
48 private ApiVersion(boolean isCloudService) {
49 this.isCloudService = isCloudService;
52 boolean isCloudService() {
53 return isCloudService;
57 /**
58 * The default maximum size a request RPC can be.
60 static final int DEFAULT_RPC_SIZE_LIMIT_BYTES = 1024 * 1024;
62 /**
63 * The default maximum number of keys allowed in a Get request.
65 static final int DEFAULT_MAX_BATCH_GET_KEYS = 1000;
67 /**
68 * The default maximum number of entities allowed in a Put or Delete request.
70 static final int DEFAULT_MAX_BATCH_WRITE_ENTITIES = 500;
72 /**
73 * Name of a system property that users can specify to control the default
74 * max entity groups per rpc.
76 static final String DEFAULT_MAX_ENTITY_GROUPS_PER_RPC_SYS_PROP =
77 "appengine.datastore.defaultMaxEntityGroupsPerRpc";
79 /**
80 * The default number of max entity groups per rpc.
82 static final int DEFAULT_MAX_ENTITY_GROUPS_PER_RPC = getDefaultMaxEntityGroupsPerRpc();
84 static final String CALLBACKS_CONFIG_SYS_PROP = "appengine.datastore.callbacksConfig";
86 static volatile DatastoreCallbacks CALLBACKS = null;
88 /**
89 * Keep in sync with
90 * com.google.appengine.tools.compilation.DatastoreCallbacksProcessor.CALLBACKS_CONFIG_FILE
92 private static final String CALLBACKS_CONFIG_FILE = "/META-INF/datastorecallbacks.xml";
94 static int getDefaultMaxEntityGroupsPerRpc() {
95 return getDefaultMaxEntityGroupsPerRpc(DEFAULT_MAX_ENTITY_GROUPS_PER_RPC_SYS_PROP, 10);
98 static final String DATASTORE_SERVICE_VERSION_SYS_PROP = "appengine.datastore.v1service";
99 static final String FORCE_LOCAL_CLOUD_DATASTORE_V1_SYS_PROP_VAL = "force";
102 * Returns the value of the given system property as an int, or return the
103 * default value if there is no property with that name set.
105 static int getDefaultMaxEntityGroupsPerRpc(String sysPropName, int defaultVal) {
106 String sysPropVal = System.getProperty(sysPropName);
107 return sysPropVal == null ? defaultVal : Integer.parseInt(sysPropVal);
110 private static InputStream getCallbacksConfigInputStream() {
111 InputStream is;
112 String callbacksConfig = System.getProperty(CALLBACKS_CONFIG_SYS_PROP);
113 if (callbacksConfig != null) {
114 is = new ByteArrayInputStream(callbacksConfig.getBytes());
115 } else {
116 is = DatastoreServiceConfig.class.getResourceAsStream(CALLBACKS_CONFIG_FILE);
118 return is;
121 private final DatastoreCallbacks instanceDatastoreCallbacks;
123 private ImplicitTransactionManagementPolicy implicitTransactionManagementPolicy =
124 ImplicitTransactionManagementPolicy.NONE;
126 private ReadPolicy readPolicy = new ReadPolicy(Consistency.STRONG);
128 private Double deadline;
130 private AppIdNamespace appIdNamespace;
132 int maxRpcSizeBytes = DEFAULT_RPC_SIZE_LIMIT_BYTES;
133 int maxBatchWriteEntities = DEFAULT_MAX_BATCH_WRITE_ENTITIES;
134 int maxBatchReadEntities = DEFAULT_MAX_BATCH_GET_KEYS;
135 int maxBatchAllocateIdKeys = DEFAULT_MAX_BATCH_WRITE_ENTITIES;
136 int maxEntityGroupsPerRpc = DEFAULT_MAX_ENTITY_GROUPS_PER_RPC;
138 private EntityCacheConfig entityCacheConfig = null;
141 * If set, overrides the service API version.
143 private ApiVersion apiVersion = null;
145 private CloudDatastoreV1Proxy cloudDatastoreV1Proxy = null;
148 * Cannot be directly instantiated, use {@link Builder} instead.
150 * @param datastoreCallbacks the callback methods to invoke on specific datastore operations.
151 * If null the datastore callbacks are derived from {@link #getCallbacksConfigInputStream}.
153 private DatastoreServiceConfig( DatastoreCallbacks datastoreCallbacks) {
154 instanceDatastoreCallbacks = datastoreCallbacks;
158 * Copy constructor
160 DatastoreServiceConfig(DatastoreServiceConfig config) {
161 implicitTransactionManagementPolicy = config.implicitTransactionManagementPolicy;
162 readPolicy = config.readPolicy;
163 deadline = config.deadline;
164 maxRpcSizeBytes = config.maxRpcSizeBytes;
165 maxBatchWriteEntities = config.maxBatchWriteEntities;
166 maxBatchReadEntities = config.maxBatchReadEntities;
167 maxEntityGroupsPerRpc = config.maxEntityGroupsPerRpc;
168 instanceDatastoreCallbacks = config.instanceDatastoreCallbacks;
169 appIdNamespace = config.appIdNamespace;
170 apiVersion = config.apiVersion;
171 cloudDatastoreV1Proxy = config.cloudDatastoreV1Proxy;
175 * Sets the implicit transaction management policy.
176 * @param p the implicit transaction management policy to set.
177 * @return {@code this} (for chaining)
179 public DatastoreServiceConfig implicitTransactionManagementPolicy(
180 ImplicitTransactionManagementPolicy p) {
181 if (p == null) {
182 throw new NullPointerException("implicit transaction management policy must not be null");
184 implicitTransactionManagementPolicy = p;
185 return this;
189 * Sets the read policy.
190 * @param readPolicy the read policy to set.
191 * @return {@code this} (for chaining)
193 public DatastoreServiceConfig readPolicy(ReadPolicy readPolicy) {
194 if (readPolicy == null) {
195 throw new NullPointerException("read policy must not be null");
197 this.readPolicy = readPolicy;
198 return this;
202 * Sets the deadline, in seconds, for all rpcs initiated by the
203 * {@link DatastoreService} with which this config is associated.
204 * @param deadline the deadline to set.
205 * @throws IllegalArgumentException if deadline is not positive
206 * @return {@code this} (for chaining)
208 public DatastoreServiceConfig deadline(double deadline) {
209 if (deadline <= 0.0) {
210 throw new IllegalArgumentException("deadline must be > 0, got " + deadline);
212 this.deadline = deadline;
213 return this;
216 DatastoreServiceConfig appIdNamespace(AppIdNamespace appIdNamespace) {
217 this.appIdNamespace = appIdNamespace;
218 return this;
222 * Sets the maximum number of entities that can be modified in a single RPC.
223 * @param maxBatchWriteEntities the limit to set
224 * @throws IllegalArgumentException if maxBatchWriteEntities is not greater
225 * than zero
226 * @return {@code this} (for chaining)
228 DatastoreServiceConfig maxBatchWriteEntities(int maxBatchWriteEntities) {
229 if (maxBatchWriteEntities <= 0) {
230 throw new IllegalArgumentException("maxBatchWriteEntities must be > 0, got "
231 + maxBatchWriteEntities);
233 this.maxBatchWriteEntities = maxBatchWriteEntities;
234 return this;
238 * Sets the maximum number of entities that can be read in a single RPC.
239 * @param maxBatchReadEntities the limit to set
240 * @throws IllegalArgumentException if maxBatchReadEntities is not greater
241 * than zero
242 * @return {@code this} (for chaining)
244 DatastoreServiceConfig maxBatchReadEntities(int maxBatchReadEntities) {
245 if (maxBatchReadEntities <= 0) {
246 throw new IllegalArgumentException("maxBatchReadEntities must be > 0, got "
247 + maxBatchReadEntities);
249 this.maxBatchReadEntities = maxBatchReadEntities;
250 return this;
254 * Sets the maximum size in bytes an RPC can be.
256 * The size of the request can be exceeded if the RPC cannot be split enough
257 * to respect this limit. However there may be a hard limit on the RPC which,
258 * if exceeded, will cause an exception to be thrown.
260 * @param maxRpcSizeBytes the limit to set
261 * @throws IllegalArgumentException if maxRpcSizeBytes is not positive
262 * @return {@code this} (for chaining)
264 DatastoreServiceConfig maxRpcSizeBytes(int maxRpcSizeBytes) {
265 if (maxRpcSizeBytes < 0) {
266 throw new IllegalArgumentException("maxRpcSizeBytes must be >= 0, got "
267 + maxRpcSizeBytes);
269 this.maxRpcSizeBytes = maxRpcSizeBytes;
270 return this;
274 * Sets the maximum number of entity groups that can be represented in a
275 * single rpc.
277 * For a non-transactional operation that involves more entity groups than the
278 * maximum, the operation will be performed by executing multiple, asynchronous
279 * rpcs to the datastore, each of which has no more entity groups represented
280 * than the maximum. So, if a put() operation has 8 entity groups and the
281 * maximum is 3, we will send 3 rpcs, 2 with 3 entity groups and 1 with 2
282 * entity groups. This is a performance optimization - in many cases
283 * multiple, small, asynchronous rpcs will finish faster than a single large
284 * asynchronous rpc. The optimal value for this property will be
285 * application-specific, so experimentation is encouraged.
287 * @param maxEntityGroupsPerRpc the maximum number of entity groups per rpc
288 * @throws IllegalArgumentException if maxEntityGroupsPerRpc is not greater
289 * than zero
290 * @return {@code this} (for chaining)
292 public DatastoreServiceConfig maxEntityGroupsPerRpc(int maxEntityGroupsPerRpc) {
293 if (maxEntityGroupsPerRpc <= 0) {
294 throw new IllegalArgumentException("maxEntityGroupsPerRpc must be > 0, got "
295 + maxEntityGroupsPerRpc);
297 this.maxEntityGroupsPerRpc = maxEntityGroupsPerRpc;
298 return this;
302 * Sets the entity cache configuration to use for entity caching.
304 * To successfully use entity caching it is important that all application code that updates
305 * or reads the same set of entities uses a {@code DatastoreServiceConfig} with the same entity
306 * cache configuration.
308 * By default the datastore service does not do any entity caching.
310 * @param entityCacheConfig the entity cache configuration to use for entity caching.
311 * @return {@code this} (for chaining)
313 DatastoreServiceConfig entityCacheConfig(EntityCacheConfig entityCacheConfig) {
314 checkNotNull(entityCacheConfig, "The entityCacheConfig argument can not be null");
315 this.entityCacheConfig = entityCacheConfig;
316 return this;
320 * @return The {@code ImplicitTransactionManagementPolicy} to use.
322 public ImplicitTransactionManagementPolicy getImplicitTransactionManagementPolicy() {
323 return implicitTransactionManagementPolicy;
327 * @return The {@code ReadPolicy} to use.
329 public ReadPolicy getReadPolicy() {
330 return readPolicy;
334 * @return The maximum number of entity groups per rpc.
336 public Integer getMaxEntityGroupsPerRpc() {
337 return getMaxEntityGroupsPerRpcInternal();
340 int getMaxEntityGroupsPerRpcInternal() {
341 return maxEntityGroupsPerRpc;
345 * @return The deadline to use. Can be {@code null}.
347 public Double getDeadline() {
348 return deadline;
351 AppIdNamespace getAppIdNamespace() {
352 return appIdNamespace == null ? DatastoreApiHelper.getCurrentAppIdNamespace() : appIdNamespace;
355 DatastoreCallbacks getDatastoreCallbacks() {
356 if (instanceDatastoreCallbacks != null) {
357 return instanceDatastoreCallbacks;
360 if (CALLBACKS == null) {
361 InputStream is = getCallbacksConfigInputStream();
362 if (is == null) {
363 CALLBACKS = DatastoreCallbacks.NoOpDatastoreCallbacks.INSTANCE;
364 } else {
365 CALLBACKS = new DatastoreCallbacksImpl(is, false);
368 return CALLBACKS;
372 * @return the {@link EntityCacheConfig} to use or {@code null} if no entity cache configuration
373 * was specified.
375 EntityCacheConfig getEntityCacheConfig() {
376 return entityCacheConfig;
380 * Datastore service API version to use.
382 ApiVersion getApiVersion() {
383 if (apiVersion != null) {
384 return apiVersion;
387 boolean useLocalCloudDatastoreV1 = FORCE_LOCAL_CLOUD_DATASTORE_V1_SYS_PROP_VAL.equals(
388 System.getProperty(DATASTORE_SERVICE_VERSION_SYS_PROP));
389 boolean useCloudDatastore = Boolean.valueOf(EnvProxy.getenv("DATASTORE_USE_CLOUD_DATASTORE"));
391 ApiVersion localApiVersion = ApiVersion.V3;
392 if (useLocalCloudDatastoreV1 && useCloudDatastore) {
393 throw new IllegalArgumentException("Cannot use both local and remote Cloud Datastore APIs.");
394 } else if (useLocalCloudDatastoreV1) {
395 localApiVersion = ApiVersion.CLOUD_DATASTORE_V1_VIA_API_PROXY;
396 } else if (useCloudDatastore) {
397 localApiVersion = ApiVersion.CLOUD_DATASTORE_V1_REMOTE;
400 apiVersion = localApiVersion;
401 return apiVersion;
404 DatastoreServiceConfig setApiVersion( ApiVersion apiVersion) {
405 this.apiVersion = apiVersion;
406 return this;
409 ApiProxy.ApiConfig constructApiConfig() {
410 ApiProxy.ApiConfig apiConfig = new ApiProxy.ApiConfig();
411 apiConfig.setDeadlineInSeconds(deadline);
412 return apiConfig;
414 CloudDatastoreV1Proxy getCloudDatastoreV1Proxy() {
415 return cloudDatastoreV1Proxy;
419 * Contains static creation methods for {@link DatastoreServiceConfig}.
421 public static final class Builder {
424 * Create a {@link DatastoreServiceConfig} with the given implicit
425 * transaction management policy.
426 * @param p the implicit transaction management policy to set.
427 * @return The newly created DatastoreServiceConfig instance.
429 public static DatastoreServiceConfig withImplicitTransactionManagementPolicy(
430 ImplicitTransactionManagementPolicy p) {
431 return withDefaults().implicitTransactionManagementPolicy(p);
435 * Create a {@link DatastoreServiceConfig} with the given read
436 * policy.
437 * @param readPolicy the read policy to set.
438 * @return The newly created DatastoreServiceConfig instance.
440 public static DatastoreServiceConfig withReadPolicy(ReadPolicy readPolicy) {
441 return withDefaults().readPolicy(readPolicy);
445 * Create a {@link DatastoreServiceConfig} with the given deadline, in
446 * seconds.
447 * @param deadline the deadline to set.
448 * @throws IllegalArgumentException if deadline is not positive
449 * @return The newly created DatastoreServiceConfig instance.
451 public static DatastoreServiceConfig withDeadline(double deadline) {
452 return withDefaults().deadline(deadline);
456 * Create a {@link DatastoreServiceConfig} with the given maximum entity
457 * groups per rpc.
458 * @param maxEntityGroupsPerRpc the maximum entity groups per rpc to set.
459 * @return The newly created DatastoreServiceConfig instance.
461 * @see DatastoreServiceConfig#maxEntityGroupsPerRpc(int)
463 public static DatastoreServiceConfig withMaxEntityGroupsPerRpc(int maxEntityGroupsPerRpc) {
464 return withDefaults().maxEntityGroupsPerRpc(maxEntityGroupsPerRpc);
468 * Create a {@link DatastoreServiceConfig} with the given entity cache configuration.
470 * @param entityCacheConfig the entity cache configuration to use for entity caching.
472 * @see DatastoreServiceConfig#entityCacheConfig
474 static DatastoreServiceConfig withEntityCacheConfig(
475 EntityCacheConfig entityCacheConfig) {
476 return withDefaults().entityCacheConfig(entityCacheConfig);
480 * Helper method for creating a {@link DatastoreServiceConfig} instance with the
481 * specified {@code datastoreCallbacks}. The callbacks defined for the application are
482 * bypassed and the specified callbacks are used instead.
484 * @return The newly created DatastoreServiceConfig instance.
486 static DatastoreServiceConfig withDatastoreCallbacks(DatastoreCallbacks datastoreCallbacks) {
487 Preconditions.checkNotNull(datastoreCallbacks);
488 return new DatastoreServiceConfig(datastoreCallbacks);
492 * Helper method for creating a {@link DatastoreServiceConfig}
493 * instance with default values: Implicit transactions are disabled, reads
494 * execute with {@link Consistency#STRONG}, and no deadline is
495 * provided. When no deadline is provided, datastore rpcs execute with the
496 * system-defined deadline.
498 * @return The newly created DatastoreServiceConfig instance.
500 public static DatastoreServiceConfig withDefaults() {
501 return new DatastoreServiceConfig((DatastoreCallbacks) null);
504 private Builder() {}