/** * Opens a sequence handle, adding the sequence record if appropriate. * * @throws IllegalArgumentException via Database.openSequence. * @throws IllegalStateException via Database.openSequence. */ Sequence(Database db, Transaction txn, DatabaseEntry key, SequenceConfig config) throws SequenceNotFoundException, SequenceExistsException { if (db.getDatabaseImpl().getSortedDuplicates()) { throw new UnsupportedOperationException( "Sequences not supported in databases configured for " + "duplicates"); } SequenceConfig useConfig = (config != null) ? config : SequenceConfig.DEFAULT; if (useConfig.getRangeMin() >= useConfig.getRangeMax()) { throw new IllegalArgumentException("Minimum sequence value must be less than the maximum"); } if (useConfig.getInitialValue() > useConfig.getRangeMax() || useConfig.getInitialValue() < useConfig.getRangeMin()) { throw new IllegalArgumentException("Initial sequence value is out of range"); } if (useConfig.getRangeMin() > useConfig.getRangeMax() - useConfig.getCacheSize()) { throw new IllegalArgumentException("The cache size is larger than the sequence range"); } if (useConfig.getAutoCommitNoSync()) { autoCommitConfig = DbInternal.getDefaultTxnConfig(db.getEnvironment()).clone(); autoCommitConfig.overrideDurability(Durability.COMMIT_NO_SYNC); } else { /* Use the environment's default transaction config. */ autoCommitConfig = null; } this.db = db; this.key = copyEntry(key); logger = db.getEnvironment().getEnvironmentImpl().getLogger(); /* Perform an auto-commit transaction to create the sequence. */ Locker locker = null; Cursor cursor = null; OperationStatus status = OperationStatus.NOTFOUND; try { locker = LockerFactory.getReadableLocker( db.getEnvironment(), txn, db.isTransactional(), false /*retainNonTxnLocks*/, false /*readCommitedIsolation*/); cursor = new Cursor(db, locker, null); boolean sequenceExists = readData(cursor, null); boolean isWritableLocker = !db.getConfig().getTransactional() || (locker.isTransactional() && !DbInternal.getEnvironmentImpl(db.getEnvironment()).isReplicated()); if (sequenceExists) { if (useConfig.getAllowCreate() && useConfig.getExclusiveCreate()) { throw new SequenceExistsException( "ExclusiveCreate=true and the sequence record " + "already exists."); } } else { if (useConfig.getAllowCreate()) { if (!isWritableLocker) { if (cursor != null) { cursor.close(); } locker.operationEnd(OperationStatus.SUCCESS); locker = LockerFactory.getWritableLocker( db.getEnvironment(), txn, db.isTransactional(), false, db.getDatabaseImpl().isReplicated(), autoCommitConfig); cursor = new Cursor(db, locker, null); } /* Get the persistent fields from the config. */ rangeMin = useConfig.getRangeMin(); rangeMax = useConfig.getRangeMax(); increment = !useConfig.getDecrement(); wrapAllowed = useConfig.getWrap(); storedValue = useConfig.getInitialValue(); /* * To avoid dependence on SerializableIsolation, try * putNoOverwrite first. If it fails, then try to get an * existing record. */ status = cursor.putNoOverwrite(key, makeData()); if (!readData(cursor, null)) { /* A retry loop should be performed here. */ throw new IllegalStateException("Sequence record removed during openSequence."); } status = OperationStatus.SUCCESS; } else { throw new SequenceNotFoundException( "AllowCreate=false and the sequence record " + "does not exist."); } } } finally { if (cursor != null) { cursor.close(); } if (locker != null) { locker.operationEnd(status); } } /* * cacheLast is initialized such that the cache will be considered * empty the first time get() is called. */ cacheSize = useConfig.getCacheSize(); cacheValue = storedValue; cacheLast = increment ? (storedValue - 1) : (storedValue + 1); }