1 // Copyright 2010 Google Inc. All Rights Reserved.
2 package com
.google
.appengine
.api
.datastore
;
4 import com
.google
.appengine
.api
.datastore
.ReadPolicy
.Consistency
;
5 import com
.google
.common
.base
.Preconditions
;
7 import java
.io
.ByteArrayInputStream
;
8 import java
.io
.InputStream
;
11 * User-configurable properties of the datastore.
14 * The recommended way to instantiate a {@code DatastoreServiceConfig} object
15 * is to statically import {@link Builder}.* and invoke a static creation
16 * method followed by an instance mutator (if needed):
19 * import static com.google.appengine.api.datastore.DatastoreServiceConfig.Builder.*;
20 * import com.google.appengine.api.datastore.ReadPolicy.Consistency;
24 * // eventually consistent reads
25 * DatastoreServiceConfig config = withReadPolicy(new ReadPolicy(Consistency.EVENTUAL));
27 * // eventually consistent reads with a 5 second deadline
28 * DatastoreServiceConfig config =
29 * withReadPolicy(new ReadPolicy(Consistency.EVENTUAL)).deadline(5.0);
33 public final class DatastoreServiceConfig
{
35 * The default maximum size a request RPC can be.
37 static final int DEFAULT_RPC_SIZE_LIMIT_BYTES
= 1024 * 1024;
40 * The default maximum number of keys allowed in a Get request.
42 static final int DEFAULT_MAX_BATCH_GET_KEYS
= 1000;
45 * The default maximum number of entities allowed in a Put or Delete request.
47 static final int DEFAULT_MAX_BATCH_WRITE_ENTITIES
= 500;
50 * Name of a system property that users can specify to control the default
51 * max entity groups per rpc.
53 static final String DEFAULT_MAX_ENTITY_GROUPS_PER_RPC_SYS_PROP
=
54 "appengine.datastore.defaultMaxEntityGroupsPerRpc";
57 * The default number of max entity groups per rpc.
59 static final int DEFAULT_MAX_ENTITY_GROUPS_PER_RPC
= getDefaultMaxEntityGroupsPerRpc();
61 static final String CALLBACKS_CONFIG_SYS_PROP
= "appengine.datastore.callbacksConfig";
63 static volatile DatastoreCallbacks CALLBACKS
= null;
67 * com.google.appengine.tools.compilation.DatastoreCallbacksProcessor.CALLBACKS_CONFIG_FILE
69 private static final String CALLBACKS_CONFIG_FILE
= "/META-INF/datastorecallbacks.xml";
71 static int getDefaultMaxEntityGroupsPerRpc() {
72 return getDefaultMaxEntityGroupsPerRpc(DEFAULT_MAX_ENTITY_GROUPS_PER_RPC_SYS_PROP
, 10);
76 * Returns the value of the given system property as an int, or return the
77 * default value if there is no property with that name set.
79 static int getDefaultMaxEntityGroupsPerRpc(String sysPropName
, int defaultVal
) {
80 String sysPropVal
= System
.getProperty(sysPropName
);
81 return sysPropVal
== null ? defaultVal
: Integer
.parseInt(sysPropVal
);
84 private static InputStream
getCallbacksConfigInputStream() {
86 String callbacksConfig
= System
.getProperty(CALLBACKS_CONFIG_SYS_PROP
);
87 if (callbacksConfig
!= null) {
88 is
= new ByteArrayInputStream(callbacksConfig
.getBytes());
90 is
= DatastoreServiceConfig
.class.getResourceAsStream(CALLBACKS_CONFIG_FILE
);
95 private final DatastoreCallbacks instanceDatastoreCallbacks
;
97 private ImplicitTransactionManagementPolicy implicitTransactionManagementPolicy
=
98 ImplicitTransactionManagementPolicy
.NONE
;
100 private ReadPolicy readPolicy
= new ReadPolicy(Consistency
.STRONG
);
102 private Double deadline
;
104 private int maxRpcSizeBytes
= DEFAULT_RPC_SIZE_LIMIT_BYTES
;
105 private int maxBatchWriteEntities
= DEFAULT_MAX_BATCH_WRITE_ENTITIES
;
106 private int maxBatchReadEntities
= DEFAULT_MAX_BATCH_GET_KEYS
;
107 private int maxEntityGroupsPerRpc
= DEFAULT_MAX_ENTITY_GROUPS_PER_RPC
;
110 * Cannot be directly instantiated, use {@link Builder} instead.
112 * @param datastoreCallbacks the callback methods to invoke on specific datastore operations.
113 * If null the datastore callbacks are derived from {@link #getCallbacksConfigInputStream}.
115 private DatastoreServiceConfig( DatastoreCallbacks datastoreCallbacks
) {
116 instanceDatastoreCallbacks
= datastoreCallbacks
;
122 DatastoreServiceConfig(DatastoreServiceConfig config
) {
123 implicitTransactionManagementPolicy
= config
.implicitTransactionManagementPolicy
;
124 readPolicy
= config
.readPolicy
;
125 deadline
= config
.deadline
;
126 maxRpcSizeBytes
= config
.maxRpcSizeBytes
;
127 maxBatchWriteEntities
= config
.maxBatchWriteEntities
;
128 maxBatchReadEntities
= config
.maxBatchReadEntities
;
129 maxEntityGroupsPerRpc
= config
.maxEntityGroupsPerRpc
;
130 instanceDatastoreCallbacks
= config
.instanceDatastoreCallbacks
;
134 * Sets the implicit transaction management policy.
135 * @param p the implicit transaction management policy to set.
136 * @return {@code this} (for chaining)
138 public DatastoreServiceConfig
implicitTransactionManagementPolicy(
139 ImplicitTransactionManagementPolicy p
) {
141 throw new NullPointerException("implicit transaction management policy must not be null");
143 implicitTransactionManagementPolicy
= p
;
148 * Sets the read policy.
149 * @param readPolicy the read policy to set.
150 * @return {@code this} (for chaining)
152 public DatastoreServiceConfig
readPolicy(ReadPolicy readPolicy
) {
153 if (readPolicy
== null) {
154 throw new NullPointerException("read policy must not be null");
156 this.readPolicy
= readPolicy
;
161 * Sets the deadline, in seconds, for all rpcs initiated by the
162 * {@link DatastoreService} with which this config is associated.
163 * @param deadline the deadline to set.
164 * @throws IllegalArgumentException if deadline is not positive
165 * @return {@code this} (for chaining)
167 public DatastoreServiceConfig
deadline(double deadline
) {
168 if (deadline
<= 0.0) {
169 throw new IllegalArgumentException("deadline must be > 0, got " + deadline
);
171 this.deadline
= deadline
;
176 * Sets the maximum number of entities that can be modified in a single RPC.
177 * @param maxBatchWriteEntities the limit to set
178 * @throws IllegalArgumentException if maxBatchWriteEntities is not greater
180 * @return {@code this} (for chaining)
182 DatastoreServiceConfig
maxBatchWriteEntities(int maxBatchWriteEntities
) {
183 if (maxBatchWriteEntities
<= 0) {
184 throw new IllegalArgumentException("maxBatchWriteEntities must be > 0, got "
185 + maxBatchWriteEntities
);
187 this.maxBatchWriteEntities
= maxBatchWriteEntities
;
192 * Sets the maximum number of entities that can be read in a single RPC.
193 * @param maxBatchReadEntities the limit to set
194 * @throws IllegalArgumentException if maxBatchReadEntities is not greater
196 * @return {@code this} (for chaining)
198 DatastoreServiceConfig
maxBatchReadEntities(int maxBatchReadEntities
) {
199 if (maxBatchReadEntities
<= 0) {
200 throw new IllegalArgumentException("maxBatchReadEntities must be > 0, got "
201 + maxBatchReadEntities
);
203 this.maxBatchReadEntities
= maxBatchReadEntities
;
208 * Sets the maximum size in bytes an RPC can be.
210 * The size of the request can be exceeded if the RPC cannot be split enough
211 * to respect this limit. However there may be a hard limit on the RPC which,
212 * if exceeded, will cause an exception to be thrown.
214 * @param maxRpcSizeBytes the limit to set
215 * @throws IllegalArgumentException if maxRpcSizeBytes is not positive
216 * @return {@code this} (for chaining)
218 DatastoreServiceConfig
maxRpcSizeBytes(int maxRpcSizeBytes
) {
219 if (maxRpcSizeBytes
< 0) {
220 throw new IllegalArgumentException("maxRpcSizeBytes must be >= 0, got "
223 this.maxRpcSizeBytes
= maxRpcSizeBytes
;
228 * Sets the maximum number of entity groups that can be represented in a
231 * For a non-transactional operation that involves more entity groups than the
232 * maximum, the operation will be performed by executing multiple, asynchronous
233 * rpcs to the datastore, each of which has no more entity groups represented
234 * than the maximum. So, if a put() operation has 8 entity groups and the
235 * maximum is 3, we will send 3 rpcs, 2 with 3 entity groups and 1 with 2
236 * entity groups. This is a performance optimization - in many cases
237 * multiple, small, asynchronous rpcs will finish faster than a single large
238 * asynchronous rpc. The optimal value for this property will be
239 * application-specific, so experimentation is encouraged.
241 * @param maxEntityGroupsPerRpc the maximum number of entity groups per rpc
242 * @throws IllegalArgumentException if maxEntityGroupsPerRpc is not greater
244 * @return {@code this} (for chaining)
246 public DatastoreServiceConfig
maxEntityGroupsPerRpc(int maxEntityGroupsPerRpc
) {
247 if (maxEntityGroupsPerRpc
<= 0) {
248 throw new IllegalArgumentException("maxEntityGroupsPerRpc must be > 0, got "
249 + maxEntityGroupsPerRpc
);
251 this.maxEntityGroupsPerRpc
= maxEntityGroupsPerRpc
;
256 * @return The {@code ImplicitTransactionManagementPolicy} to use.
258 public ImplicitTransactionManagementPolicy
getImplicitTransactionManagementPolicy() {
259 return implicitTransactionManagementPolicy
;
263 * @return The {@code ReadPolicy} to use.
265 public ReadPolicy
getReadPolicy() {
270 * @return The maximum number of entity groups per rpc.
272 public Integer
getMaxEntityGroupsPerRpc() {
273 return getMaxEntityGroupsPerRpcInternal();
276 int getMaxEntityGroupsPerRpcInternal() {
277 return maxEntityGroupsPerRpc
;
281 * @return The deadline to use. Can be {@code null}.
283 public Double
getDeadline() {
287 boolean exceedsWriteLimits(int count
, int size
) {
288 return (count
> maxBatchWriteEntities
||
289 (count
> 1 && size
> maxRpcSizeBytes
));
292 boolean exceedsReadLimits(int count
, int size
) {
293 return (count
> maxBatchReadEntities
||
294 (count
> 1 && size
> maxRpcSizeBytes
));
297 DatastoreCallbacks
getDatastoreCallbacks() {
298 if (instanceDatastoreCallbacks
!= null) {
299 return instanceDatastoreCallbacks
;
302 if (CALLBACKS
== null) {
303 InputStream is
= getCallbacksConfigInputStream();
305 CALLBACKS
= DatastoreCallbacks
.NoOpDatastoreCallbacks
.INSTANCE
;
307 CALLBACKS
= new DatastoreCallbacksImpl(is
, false);
314 * Contains static creation methods for {@link DatastoreServiceConfig}.
316 public static final class Builder
{
319 * Create a {@link DatastoreServiceConfig} with the given implicit
320 * transaction management policy.
321 * @param p the implicit transaction management policy to set.
322 * @return The newly created DatastoreServiceConfig instance.
324 public static DatastoreServiceConfig
withImplicitTransactionManagementPolicy(
325 ImplicitTransactionManagementPolicy p
) {
326 return withDefaults().implicitTransactionManagementPolicy(p
);
330 * Create a {@link DatastoreServiceConfig} with the given read
332 * @param readPolicy the read policy to set.
333 * @return The newly created DatastoreServiceConfig instance.
335 public static DatastoreServiceConfig
withReadPolicy(ReadPolicy readPolicy
) {
336 return withDefaults().readPolicy(readPolicy
);
340 * Create a {@link DatastoreServiceConfig} with the given deadline, in
342 * @param deadline the deadline to set.
343 * @throws IllegalArgumentException if deadline is not positive
344 * @return The newly created DatastoreServiceConfig instance.
346 public static DatastoreServiceConfig
withDeadline(double deadline
) {
347 return withDefaults().deadline(deadline
);
351 * Create a {@link DatastoreServiceConfig} with the given maximum entity
353 * @param maxEntityGroupsPerRpc the maximum entity groups per rpc to set.
354 * @return The newly created DatastoreServiceConfig instance.
356 * @see DatastoreServiceConfig#maxEntityGroupsPerRpc(int)
358 public static DatastoreServiceConfig
withMaxEntityGroupsPerRpc(int maxEntityGroupsPerRpc
) {
359 return withDefaults().maxEntityGroupsPerRpc(maxEntityGroupsPerRpc
);
363 * Helper method for creating a {@link DatastoreServiceConfig} instance with the
364 * specified {@code datastoreCallbacks}. The callbacks defined for the application are
365 * bypassed and the specified callbacks are used instead.
367 * @return The newly created DatastoreServiceConfig instance.
369 static DatastoreServiceConfig
withDatastoreCallbacks(DatastoreCallbacks datastoreCallbacks
) {
370 Preconditions
.checkNotNull(datastoreCallbacks
);
371 return new DatastoreServiceConfig(datastoreCallbacks
);
375 * Helper method for creating a {@link DatastoreServiceConfig}
376 * instance with default values: Implicit transactions are disabled, reads
377 * execute with {@link Consistency#STRONG}, and no deadline is
378 * provided. When no deadline is provided, datastore rpcs execute with the
379 * system-defined deadline.
381 * @return The newly created DatastoreServiceConfig instance.
383 public static DatastoreServiceConfig
withDefaults() {
384 return new DatastoreServiceConfig((DatastoreCallbacks
) null);