All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.azure.cosmos.implementation.uuid.ext.FileBasedTimestampSynchronizer Maven / Gradle / Ivy

/* JUG Java Uuid Generator
 *
 * Copyright (c) 2002- Tatu Saloranta, [email protected]
 *
 * Licensed under the License specified in the file LICENSE which is
 * included with the source code.
 * You may not use this file except in compliance with the License.
 *
 * 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.azure.cosmos.implementation.uuid.ext;

import com.azure.cosmos.implementation.uuid.TimestampSynchronizer;
import com.azure.cosmos.implementation.uuid.UUIDTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * Implementation of {@link TimestampSynchronizer}, which uses file system
 * as the storage and locking mechanism.
 *

* Synchronization is achieved by obtaining an exclusive file locks on two * specified lock files, and by using the files to store first "safe" timestamp * value that the generator can use; alternating between one to use to ensure * one of them always contains a valid timestamp. Latter is needed to guard * against system clock moving backwards after UUID generator restart. *

* Note: this class will only work on JDK 1.4 and above, since it requires * NIO package to do proper file locking (as well as new opening mode for * {@link RandomAccessFile}). *

* Also note that it is assumed that the caller has taken care to synchronize * access to method to be single-threaded. As such, none of the methods * is explicitly synchronized here. */ /* * Portions Copyright (c) Microsoft Corporation */ public final class FileBasedTimestampSynchronizer extends TimestampSynchronizer { private static final Logger logger = LoggerFactory.getLogger(FileBasedTimestampSynchronizer.class); // // // Constants: /** * The default update interval is 10 seconds, meaning that the * synchronizer "reserves" next 10 seconds for generation. This * also means that the lock files need to be accessed at most * once every ten second. */ final static long DEFAULT_UPDATE_INTERVAL = 10L * 1000L; protected final static String DEFAULT_LOCK_FILE_NAME1 = "uuid1.lck"; protected final static String DEFAULT_LOCK_FILE_NAME2 = "uuid2.lck"; // // // Configuration: protected long mInterval = DEFAULT_UPDATE_INTERVAL; protected final LockedFile mLocked1, mLocked2; // // // State: /** * Flag used to indicate which of timestamp files has the most * recently succesfully updated timestamp value. True means that * mFile1 is more recent; false that mFile2 * is. */ boolean mFirstActive = false; /** * Constructor that uses default values for names of files to use * (files will get created in the current working directory), as * well as for the update frequency value (10 seconds). */ public FileBasedTimestampSynchronizer() throws IOException { this(new File(DEFAULT_LOCK_FILE_NAME1), new File(DEFAULT_LOCK_FILE_NAME2)); } public FileBasedTimestampSynchronizer(File lockFile1, File lockFile2) throws IOException { this(lockFile1, lockFile2, DEFAULT_UPDATE_INTERVAL); } public FileBasedTimestampSynchronizer(File lockFile1, File lockFile2, long interval) throws IOException { mInterval = interval; mLocked1 = new LockedFile(lockFile1); boolean ok = false; try { mLocked2 = new LockedFile(lockFile2); ok = true; } finally { if (!ok) { mLocked1.deactivate(); } } // But let's leave reading up to initialization } /* ////////////////////////////////////////////////////////////// // Configuration ////////////////////////////////////////////////////////////// */ public void setUpdateInterval(long interval) { if (interval < 1L) { throw new IllegalArgumentException("Illegal value ("+interval+"); has to be a positive integer value"); } mInterval = interval; } /* ////////////////////////////////////////////////////////////// // Implementation of the API ////////////////////////////////////////////////////////////// */ /** * This method is to be called only once by * {@link UUIDTimer}. It * should fetch the persisted timestamp value, which indicates * first timestamp value that is guaranteed NOT to have used by * a previous incarnation. If it can not determine such value, it * is to return 0L as a marker. * * @return First timestamp value that was NOT locked by lock files; * 0L to indicate that no information was read. */ @Override protected long initialize() throws IOException { long ts1 = mLocked1.readStamp(); long ts2 = mLocked2.readStamp(); long result; if (ts1 > ts2) { mFirstActive = true; result = ts1; } else { mFirstActive = false; result = ts2; } /* Hmmh. If we didn't get a time stamp (-> 0), or if written time is * ahead of current time, let's log something: */ if (result <= 0L) { logger.warn("Could not determine safe timer starting point: assuming current system time is acceptable"); } else { long now = System.currentTimeMillis(); //long diff = now - result; /* It's more suspicious if old time was ahead... although with * longer iteration values, it can be ahead without errors. So * let's base check on current iteration value: */ if ((now + mInterval) < result) { logger.warn("Safe timestamp read is {} milliseconds in future, and is greater than the inteval ({})", (result - now), mInterval); } /* Hmmh. Is there any way a suspiciously old timestamp could be * harmful? It can obviously be useless but... */ } return result; } @Override public void deactivate() throws IOException { doDeactivate(mLocked1, mLocked2); } /** * @return Timestamp value that the caller can NOT use. That is, all * timestamp values prior to (less than) this value can be used * ok, but this value and ones after can only be used by first * calling update. */ @Override public long update(long now) throws IOException { long nextAllowed = now + mInterval; /* We have to make sure to (over)write the one that is NOT * actively used, to ensure that we always have fully persisted * timestamp value, even if the write process gets interruped * half-way through. */ if (mFirstActive) { mLocked2.writeStamp(nextAllowed); } else { mLocked1.writeStamp(nextAllowed); } mFirstActive = !mFirstActive; return nextAllowed; } /* ////////////////////////////////////////////////////////////// // Internal methods ////////////////////////////////////////////////////////////// */ protected static void doDeactivate(LockedFile lf1, LockedFile lf2) { if (lf1 != null) { lf1.deactivate(); } if (lf2 != null) { lf2.deactivate(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy