diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java index 287cd48..9521b5e 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java @@ -50,6 +50,7 @@ import org.apache.hadoop.hbase.regionserver.wal.HLog; import org.apache.hadoop.hbase.regionserver.wal.WALEdit; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.ClassSize; +import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; import org.apache.hadoop.hbase.util.FSUtils; import org.apache.hadoop.hbase.util.Writables; import org.apache.hadoop.util.Progressable; @@ -362,7 +363,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ // HRegion is ready to go! this.writestate.compacting = false; - this.lastFlushTime = System.currentTimeMillis(); + this.lastFlushTime = currentTimeMillis(); LOG.info("region " + this + "/" + this.regionInfo.getEncodedName() + " available; sequence id is " + this.minSequenceId); } @@ -670,7 +671,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ } // Calculate regionid to use. Can't be less than that of parent else // it'll insert into wrong location over in .META. table: HBASE-710. - long rid = System.currentTimeMillis(); + long rid = currentTimeMillis(); if (rid < this.regionInfo.getRegionId()) { LOG.warn("Clock skew; parent regions id is " + this.regionInfo.getRegionId() + " but current time here is " + rid); @@ -835,7 +836,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ } LOG.info("Starting" + (majorCompaction? " major " : " ") + "compaction on region " + this); - long startTime = System.currentTimeMillis(); + long startTime = currentTimeMillis(); doRegionCompactionPrep(); long maxSize = -1; for (Store store: stores.values()) { @@ -846,7 +847,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ } } doRegionCompactionCleanup(); - String timeTaken = StringUtils.formatTimeDiff(System.currentTimeMillis(), + String timeTaken = StringUtils.formatTimeDiff(currentTimeMillis(), startTime); LOG.info("compaction completed on region " + this + " in " + timeTaken); } finally { @@ -948,7 +949,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ * because a Snapshot was not properly persisted. */ protected boolean internalFlushcache() throws IOException { - final long startTime = System.currentTimeMillis(); + final long startTime = currentTimeMillis(); // Clear flush flag. // Record latest flush time this.lastFlushTime = startTime; @@ -1087,7 +1088,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ } if (LOG.isDebugEnabled()) { - long now = System.currentTimeMillis(); + long now = currentTimeMillis(); LOG.debug("Finished memstore flush of ~" + StringUtils.humanReadableInt(currentMemStoreSize) + " for region " + this + " in " + (now - startTime) + "ms, sequence id=" + sequenceId + @@ -1267,7 +1268,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ */ public void delete(Map> familyMap, boolean writeToWAL) throws IOException { - long now = System.currentTimeMillis(); + long now = currentTimeMillis(); byte [] byteNow = Bytes.toBytes(now); boolean flush = false; @@ -1428,7 +1429,6 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ // If we did not pass an existing row lock, obtain a new one Integer lid = getLock(lockid, row); - byte [] now = Bytes.toBytes(System.currentTimeMillis()); try { // All edits for the given row (across all column families) must happen atomically. put(put.getFamilyMap(), writeToWAL); @@ -1470,8 +1470,6 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ checkFamily(family); get.addColumn(family, qualifier); - byte [] now = Bytes.toBytes(System.currentTimeMillis()); - // Lock row Integer lid = getLock(lockId, get.getRow()); List result = new ArrayList(); @@ -1618,7 +1616,7 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ */ private void put(final Map> familyMap, boolean writeToWAL) throws IOException { - long now = System.currentTimeMillis(); + long now = currentTimeMillis(); byte[] byteNow = Bytes.toBytes(now); boolean flush = false; this.updatesLock.readLock().lock(); @@ -2666,12 +2664,12 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ // bulid the KeyValue now: KeyValue newKv = new KeyValue(row, family, - qualifier, System.currentTimeMillis(), + qualifier, currentTimeMillis(), Bytes.toBytes(result)); // now log it: if (writeToWAL) { - long now = System.currentTimeMillis(); + long now = currentTimeMillis(); WALEdit walEdit = new WALEdit(); walEdit.add(newKv); this.log.append(regionInfo, regionInfo.getTableDesc().getName(), @@ -2697,6 +2695,9 @@ public class HRegion implements HConstants, HeapSize { // , Writable{ return result; } + private long currentTimeMillis() { + return EnvironmentEdgeManager.getDelegate().currentTimeMillis(); + } // // New HBASE-880 Helpers diff --git a/src/main/java/org/apache/hadoop/hbase/util/DefaultEnvironmentEdge.java b/src/main/java/org/apache/hadoop/hbase/util/DefaultEnvironmentEdge.java new file mode 100644 index 0000000..66f9192 --- /dev/null +++ b/src/main/java/org/apache/hadoop/hbase/util/DefaultEnvironmentEdge.java @@ -0,0 +1,37 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +/** + * Default implementation of an environment edge. + */ +public class DefaultEnvironmentEdge implements EnvironmentEdge { + + + /** + * {@inheritDoc} + *

+ * This implementation returns {@link System#currentTimeMillis()} + */ + @Override + public long currentTimeMillis() { + return System.currentTimeMillis(); + } +} diff --git a/src/main/java/org/apache/hadoop/hbase/util/EnvironmentEdge.java b/src/main/java/org/apache/hadoop/hbase/util/EnvironmentEdge.java new file mode 100644 index 0000000..81df224 --- /dev/null +++ b/src/main/java/org/apache/hadoop/hbase/util/EnvironmentEdge.java @@ -0,0 +1,34 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +/** + * Has some basic interaction with the environment. Alternate implementations + * can be used where required (eg in tests). + */ +public interface EnvironmentEdge { + + /** + * Returns the currentTimeMillis. + * + * @return currentTimeMillis. + */ + long currentTimeMillis(); +} diff --git a/src/main/java/org/apache/hadoop/hbase/util/EnvironmentEdgeManager.java b/src/main/java/org/apache/hadoop/hbase/util/EnvironmentEdgeManager.java new file mode 100644 index 0000000..6d98351 --- /dev/null +++ b/src/main/java/org/apache/hadoop/hbase/util/EnvironmentEdgeManager.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * Manages a singleton instance of the environment edge. + */ +public class EnvironmentEdgeManager { + private static EnvironmentEdge delegate = new DefaultEnvironmentEdge(); + private static final ReentrantLock lock = new ReentrantLock(); + + private EnvironmentEdgeManager() { + + } + + /** + * Retrieves the singleton instance of the {@link EnvironmentEdge} that is + * being managed. + * + * @return the edge. + */ + public synchronized static EnvironmentEdge getDelegate() { + if (delegate == null) { + injectEdge(new DefaultEnvironmentEdge()); + } + return delegate; + } + + /** + * Resets the managed instance to the default instance: {@link + * DefaultEnvironmentEdge}. + */ + static void reset() { + injectEdge(new DefaultEnvironmentEdge()); + } + + /** + * Injects the given edge such that it becomes the managed entity. + * + * @param edge the new edge. + */ + static void injectEdge(EnvironmentEdge edge) { + lock.lock(); + try { + delegate = edge; + } finally { + lock.unlock(); + } + } + +} diff --git a/src/main/java/org/apache/hadoop/hbase/util/IncrementingEnvironmentEdge.java b/src/main/java/org/apache/hadoop/hbase/util/IncrementingEnvironmentEdge.java new file mode 100644 index 0000000..e105b77 --- /dev/null +++ b/src/main/java/org/apache/hadoop/hbase/util/IncrementingEnvironmentEdge.java @@ -0,0 +1,39 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +/** + * Uses an incrementing algorithm instead of the default. + */ +public class IncrementingEnvironmentEdge implements EnvironmentEdge { + + private long timeIncrement = 1; + + /** + * {@inheritDoc} + *

+ * This method increments a known value for the current time each time this + * method is called. The first value is 1. + */ + @Override + public synchronized long currentTimeMillis() { + return timeIncrement++; + } +} diff --git a/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java b/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java index d3716d6..af64ac4 100644 --- a/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java +++ b/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java @@ -24,12 +24,12 @@ import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HBaseTestCase; +import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; -import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Put; @@ -45,6 +45,8 @@ import org.apache.hadoop.hbase.filter.PrefixFilter; import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; import org.apache.hadoop.hbase.regionserver.HRegion.RegionScanner; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper; +import org.apache.hadoop.hbase.util.IncrementingEnvironmentEdge; import java.io.IOException; import java.util.ArrayList; @@ -89,6 +91,12 @@ public class TestHRegion extends HBaseTestCase { super.setUp(); } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + EnvironmentEdgeManagerTestHelper.reset(); + } + ////////////////////////////////////////////////////////////////////////////// // New tests that doesn't spin up a mini cluster but rather just test the // individual code pieces in the HRegion. Putting files locally in @@ -524,6 +532,7 @@ public class TestHRegion extends HBaseTestCase { byte [][] families = {fam}; String method = this.getName(); initHRegion(tableName, method, families); + EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge()); byte [] row = Bytes.toBytes("table_name"); // column names @@ -562,9 +571,6 @@ public class TestHRegion extends HBaseTestCase { result = region.get(get, null); assertEquals(1, result.size()); - // Sleep to ensure timestamp of next Put is bigger than previous delete - Thread.sleep(10); - // Assert that after a delete, I can put. put = new Put(row); put.add(fam, splitA, Bytes.toBytes("reference_A")); @@ -578,9 +584,6 @@ public class TestHRegion extends HBaseTestCase { region.delete(delete, null, false); assertEquals(0, region.get(get, null).size()); - // Sleep to ensure timestamp of next Put is bigger than previous delete - Thread.sleep(10); - region.put(new Put(row).add(fam, splitA, Bytes.toBytes("reference_A"))); result = region.get(get, null); assertEquals(1, result.size()); @@ -676,16 +679,14 @@ public class TestHRegion extends HBaseTestCase { public void doTestDelete_AndPostInsert(Delete delete) throws IOException, InterruptedException { initHRegion(tableName, getName(), fam1); + EnvironmentEdgeManagerTestHelper.injectEdge(new IncrementingEnvironmentEdge()); Put put = new Put(row); put.add(fam1, qual1, value1); region.put(put); - Thread.sleep(10); - // now delete the value: region.delete(delete, null, true); - Thread.sleep(10); // ok put data: put = new Put(row); diff --git a/src/test/java/org/apache/hadoop/hbase/util/EnvironmentEdgeManagerTestHelper.java b/src/test/java/org/apache/hadoop/hbase/util/EnvironmentEdgeManagerTestHelper.java new file mode 100644 index 0000000..730f4e3 --- /dev/null +++ b/src/test/java/org/apache/hadoop/hbase/util/EnvironmentEdgeManagerTestHelper.java @@ -0,0 +1,36 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +/** + * Used by tests to inject an edge into the manager. The intent is to minimise + * the use of the injectEdge method giving it default permissions, but in + * testing we may need to use this functionality elsewhere. + */ +public class EnvironmentEdgeManagerTestHelper { + + public static void reset() { + EnvironmentEdgeManager.reset(); + } + + public static void injectEdge(EnvironmentEdge edge) { + EnvironmentEdgeManager.injectEdge(edge); + } +} diff --git a/src/test/java/org/apache/hadoop/hbase/util/TestDefaultEnvironmentEdge.java b/src/test/java/org/apache/hadoop/hbase/util/TestDefaultEnvironmentEdge.java new file mode 100644 index 0000000..155897b --- /dev/null +++ b/src/test/java/org/apache/hadoop/hbase/util/TestDefaultEnvironmentEdge.java @@ -0,0 +1,50 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +import org.junit.Test; + +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +/** + * Tests to make sure that the default environment edge conforms to appropriate + * behaviour. + */ +public class TestDefaultEnvironmentEdge { + + @Test + public void testGetCurrentTimeUsesSystemClock() { + DefaultEnvironmentEdge edge = new DefaultEnvironmentEdge(); + long systemTime = System.currentTimeMillis(); + long edgeTime = edge.currentTimeMillis(); + assertTrue("System time must be either the same or less than the edge time", + systemTime < edgeTime || systemTime == edgeTime); + try { + Thread.sleep(1); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + long secondEdgeTime = edge.currentTimeMillis(); + assertTrue("Second time must be greater than the first", + secondEdgeTime > edgeTime); + } + +} diff --git a/src/test/java/org/apache/hadoop/hbase/util/TestEnvironmentEdgeManager.java b/src/test/java/org/apache/hadoop/hbase/util/TestEnvironmentEdgeManager.java new file mode 100644 index 0000000..2f1318c --- /dev/null +++ b/src/test/java/org/apache/hadoop/hbase/util/TestEnvironmentEdgeManager.java @@ -0,0 +1,44 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class TestEnvironmentEdgeManager { + + @Test + public void testManageSingleton() { + EnvironmentEdge edge = EnvironmentEdgeManager.getDelegate(); + assertNotNull(edge); + assertTrue(edge instanceof DefaultEnvironmentEdge); + EnvironmentEdgeManager.reset(); + EnvironmentEdge edge2 = EnvironmentEdgeManager.getDelegate(); + assertFalse(edge == edge2); + IncrementingEnvironmentEdge newEdge = new IncrementingEnvironmentEdge(); + EnvironmentEdgeManager.injectEdge(newEdge); + assertEquals(newEdge, EnvironmentEdgeManager.getDelegate()); + } + +} diff --git a/src/test/java/org/apache/hadoop/hbase/util/TestIncrementingEnvironmentEdge.java b/src/test/java/org/apache/hadoop/hbase/util/TestIncrementingEnvironmentEdge.java new file mode 100644 index 0000000..6bb5910 --- /dev/null +++ b/src/test/java/org/apache/hadoop/hbase/util/TestIncrementingEnvironmentEdge.java @@ -0,0 +1,40 @@ +/* + * Copyright 2010 The Apache Software Foundation + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.hadoop.hbase.util; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * Tests that the incrementing environment edge increments time instead of using + * the default. + */ +public class TestIncrementingEnvironmentEdge { + + @Test + public void testGetCurrentTimeUsesSystemClock() { + IncrementingEnvironmentEdge edge = new IncrementingEnvironmentEdge(); + assertEquals(1, edge.currentTimeMillis()); + assertEquals(2, edge.currentTimeMillis()); + assertEquals(3, edge.currentTimeMillis()); + assertEquals(4, edge.currentTimeMillis()); + } +}