1 8 9 package com.sleepycat.je; 10 11 import java.math.BigInteger ; 12 import java.nio.ByteBuffer ; 13 import java.util.logging.Level ; 14 import java.util.logging.Logger ; 15 16 import com.sleepycat.je.log.LogUtils; 17 import com.sleepycat.je.txn.Locker; 18 import com.sleepycat.je.txn.LockerFactory; 19 20 24 public class Sequence { 25 26 private static final byte FLAG_INCR = ((byte) 0x1); 27 private static final byte FLAG_WRAP = ((byte) 0x2); 28 private static final byte FLAG_OVER = ((byte) 0x4); 29 30 31 private static final int MAX_DATA_SIZE = 50; 32 33 34 private static final byte CURRENT_VERSION = 0; 35 36 37 private Database db; 38 private DatabaseEntry key; 39 40 41 private boolean wrapAllowed; 42 private boolean increment; 43 private boolean overflow; 44 private long rangeMin; 45 private long rangeMax; 46 private long storedValue; 47 48 49 private int cacheSize; 50 private long cacheValue; 51 private long cacheLast; 52 private int nGets; 53 private int nCachedGets; 54 private TransactionConfig autoCommitConfig; 55 private Logger logger; 56 57 66 67 70 Sequence(Database db, 71 Transaction txn, 72 DatabaseEntry key, 73 SequenceConfig config) 74 throws DatabaseException { 75 76 if (db.getDatabaseImpl().getSortedDuplicates()) { 77 throw new IllegalArgumentException 78 ("Sequences not supported in databases configured for " + 79 "duplicates"); 80 } 81 82 SequenceConfig useConfig = (config != null) ? 83 config : SequenceConfig.DEFAULT; 84 85 if (useConfig.getRangeMin() >= useConfig.getRangeMax()) { 86 throw new IllegalArgumentException 87 ("Minimum sequence value must be less than the maximum"); 88 } 89 90 if (useConfig.getInitialValue() > useConfig.getRangeMax() || 91 useConfig.getInitialValue() < useConfig.getRangeMin()) { 92 throw new IllegalArgumentException 93 ("Initial sequence value is out of range"); 94 } 95 96 if (useConfig.getRangeMin() > 97 useConfig.getRangeMax() - useConfig.getCacheSize()) { 98 throw new IllegalArgumentException 99 ("The cache size is larger than the sequence range"); 100 } 101 102 if (useConfig.getAutoCommitNoSync()) { 103 autoCommitConfig = new TransactionConfig(); 104 autoCommitConfig.setNoSync(true); 105 } else { 106 107 autoCommitConfig = null; 108 } 109 110 this.db = db; 111 this.key = copyEntry(key); 112 logger = db.getEnvironment().getEnvironmentImpl().getLogger(); 113 114 115 Locker locker = null; 116 Cursor cursor = null; 117 OperationStatus status = OperationStatus.NOTFOUND; 118 try { 119 locker = LockerFactory.getWritableLocker 120 (db.getEnvironment(), txn, db.isTransactional(), 121 false, autoCommitConfig); 122 123 cursor = new Cursor(db, locker, null); 124 125 if (useConfig.getAllowCreate()) { 126 127 128 rangeMin = useConfig.getRangeMin(); 129 rangeMax = useConfig.getRangeMax(); 130 increment = !useConfig.getDecrement(); 131 wrapAllowed = useConfig.getWrap(); 132 storedValue = useConfig.getInitialValue(); 133 134 139 status = cursor.putNoOverwrite(key, makeData()); 140 141 if (status == OperationStatus.KEYEXIST) { 142 if (useConfig.getExclusiveCreate()) { 143 throw new DatabaseException 144 ("ExclusiveCreate=true and the sequence record " + 145 "already exists."); 146 } 147 if (!readData(cursor, null)) { 148 throw new DatabaseException 149 ("Sequence record removed during openSequence."); 150 } 151 status = OperationStatus.SUCCESS; 152 } 153 } else { 154 155 156 if (!readData(cursor, null)) { 157 throw new DatabaseException 158 ("AllowCreate=false and the sequence record " + 159 "does not exist."); 160 } 161 status = OperationStatus.SUCCESS; 162 } 163 } finally { 164 if (cursor != null) { 165 cursor.close(); 166 } 167 if (locker != null) { 168 locker.operationEnd(status); 169 } 170 } 171 172 176 cacheSize = useConfig.getCacheSize(); 177 cacheValue = storedValue; 178 cacheLast = increment ? (storedValue - 1) : (storedValue + 1); 179 } 180 181 185 public void close() 186 throws DatabaseException { 187 188 189 } 190 191 199 public synchronized long get(Transaction txn, int delta) 200 throws DatabaseException { 201 202 203 if (delta <= 0) { 204 throw new IllegalArgumentException 205 ("Sequence delta must be greater than zero"); 206 } 207 if (rangeMin > rangeMax - delta) { 208 throw new IllegalArgumentException 209 ("Sequence delta is larger than the range"); 210 } 211 212 213 boolean cached = true; 214 boolean wrapped = false; 215 216 221 if ((increment && delta > ((cacheLast - cacheValue) + 1)) || 222 (!increment && delta > ((cacheValue - cacheLast) + 1))) { 223 224 cached = false; 225 226 231 int adjust = (delta > cacheSize) ? delta : cacheSize; 232 233 234 Locker locker = null; 235 Cursor cursor = null; 236 OperationStatus status = OperationStatus.NOTFOUND; 237 try { 238 locker = LockerFactory.getWritableLocker 239 (db.getEnvironment(), txn, db.isTransactional(), 240 false, autoCommitConfig); 241 242 cursor = new Cursor(db, locker, null); 243 244 245 readDataRequired(cursor, LockMode.RMW); 246 247 248 if (overflow) { 249 throw new DatabaseException 250 ("Sequence overflow " + storedValue); 251 } 252 253 259 BigInteger availBig; 260 if (increment) { 261 262 availBig = BigInteger.valueOf(rangeMax). 263 subtract(BigInteger.valueOf(storedValue)); 264 } else { 265 266 availBig = BigInteger.valueOf(storedValue). 267 subtract(BigInteger.valueOf(rangeMin)); 268 } 269 270 if (availBig.compareTo(BigInteger.valueOf(adjust)) < 0) { 271 272 int availInt = (int) availBig.longValue(); 273 if (availInt < delta) { 274 if (wrapAllowed) { 275 276 storedValue = increment ? rangeMin : rangeMax; 277 wrapped = true; 278 } else { 279 280 overflow = true; 281 adjust = 0; 282 } 283 } else { 284 285 290 adjust = availInt; 291 } 292 } 293 294 295 if (!increment) { 296 adjust = -adjust; 297 } 298 299 300 storedValue += adjust; 301 302 303 cursor.put(key, makeData()); 304 status = OperationStatus.SUCCESS; 305 } finally { 306 if (cursor != null) { 307 cursor.close(); 308 } 309 if (locker != null) { 310 locker.operationEnd(status); 311 } 312 } 313 314 315 cacheValue = storedValue - adjust; 316 cacheLast = storedValue + (increment ? (-1) : 1); 317 } 318 319 320 long retVal = cacheValue; 321 if (increment) { 322 cacheValue += delta; 323 } else { 324 cacheValue -= delta; 325 } 326 327 328 nGets += 1; 329 if (cached) { 330 nCachedGets += 1; 331 } 332 333 334 if (logger.isLoggable(Level.FINEST)) { 335 logger.log 336 (Level.FINEST, 337 "Sequence.get" + 338 " value=" + retVal + 339 " cached=" + cached + 340 " wrapped=" + wrapped); 341 } 342 343 return retVal; 344 } 345 346 350 public Database getDatabase() 351 throws DatabaseException { 352 353 return db; 354 } 355 356 360 public DatabaseEntry getKey() 361 throws DatabaseException { 362 363 return copyEntry(key); 364 } 365 366 370 public SequenceStats getStats(StatsConfig config) 371 throws DatabaseException { 372 373 if (config == null) { 374 config = StatsConfig.DEFAULT; 375 } 376 377 if (!config.getFast()) { 378 379 384 Cursor cursor = db.openCursor(null, null); 385 try { 386 readDataRequired(cursor, LockMode.READ_UNCOMMITTED); 387 } finally { 388 cursor.close(); 389 } 390 } 391 392 SequenceStats stats = new SequenceStats 393 (nGets, 394 nCachedGets, 395 storedValue, 396 cacheValue, 397 cacheLast, 398 rangeMin, 399 rangeMax, 400 cacheSize); 401 402 if (config.getClear()) { 403 nGets = 0; 404 nCachedGets = 0; 405 } 406 407 return stats; 408 } 409 410 414 private void readDataRequired(Cursor cursor, LockMode lockMode) 415 throws DatabaseException { 416 417 if (!readData(cursor, lockMode)) { 418 throw new DatabaseException 419 ("The sequence record has been deleted while it is open."); 420 } 421 } 422 423 427 private boolean readData(Cursor cursor, LockMode lockMode) 428 throws DatabaseException { 429 430 431 DatabaseEntry data = new DatabaseEntry(); 432 OperationStatus status = cursor.getSearchKey(key, data, lockMode); 433 if (status != OperationStatus.SUCCESS) { 434 return false; 435 } 436 ByteBuffer buf = ByteBuffer.wrap(data.getData()); 437 438 439 byte ignoreVersionForNow = buf.get(); 440 byte flags = buf.get(); 441 rangeMin = LogUtils.readLong(buf); 442 rangeMax = LogUtils.readLong(buf); 443 storedValue = LogUtils.readLong(buf); 444 445 increment = (flags & FLAG_INCR) != 0; 446 wrapAllowed = (flags & FLAG_WRAP) != 0; 447 overflow = (flags & FLAG_OVER) != 0; 448 449 return true; 450 } 451 452 455 private DatabaseEntry makeData() { 456 457 byte[] data = new byte[MAX_DATA_SIZE]; 458 ByteBuffer buf = ByteBuffer.wrap(data); 459 460 byte flags = 0; 461 if (increment) { 462 flags |= FLAG_INCR; 463 } 464 if (wrapAllowed) { 465 flags |= FLAG_WRAP; 466 } 467 if (overflow) { 468 flags |= FLAG_OVER; 469 } 470 471 buf.put(CURRENT_VERSION); 472 buf.put(flags); 473 LogUtils.writeLong(buf, rangeMin); 474 LogUtils.writeLong(buf, rangeMax); 475 LogUtils.writeLong(buf, storedValue); 476 477 return new DatabaseEntry(data, 0, buf.position()); 478 } 479 480 483 private DatabaseEntry copyEntry(DatabaseEntry entry) { 484 485 int len = entry.getSize(); 486 byte[] data; 487 if (len == 0) { 488 data = LogUtils.ZERO_LENGTH_BYTE_ARRAY; 489 } else { 490 data = new byte[len]; 491 System.arraycopy 492 (entry.getData(), entry.getOffset(), data, 0, data.length); 493 } 494 495 return new DatabaseEntry(data); 496 } 497 } 498 | Popular Tags |