PHOENIX-2520 Create DDL property for metadata update frequency

Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/59b336ec
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/59b336ec
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/59b336ec

Branch: refs/heads/calcite
Commit: 59b336ec84b7cf5189bb2e67f07f9ef20da898d9
Parents: 3e5fa3e
Author: James Taylor <jtay...@salesforce.com>
Authored: Sun Jan 17 11:24:26 2016 -0800
Committer: James Taylor <jtay...@salesforce.com>
Committed: Sun Jan 17 11:49:00 2016 -0800

----------------------------------------------------------------------
 .../org/apache/phoenix/rpc/UpdateCacheIT.java   |  92 ++++++++++----
 .../phoenix/rpc/UpdateCacheWithScnIT.java       |  17 ++-
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |   3 +-
 .../apache/phoenix/compile/FromCompiler.java    |   2 +-
 .../apache/phoenix/compile/JoinCompiler.java    |   2 +-
 .../compile/TupleProjectionCompiler.java        |   4 +-
 .../apache/phoenix/compile/UnionCompiler.java   |   2 +-
 .../coprocessor/MetaDataEndpointImpl.java       |  13 +-
 .../phoenix/coprocessor/MetaDataProtocol.java   |   2 +-
 .../coprocessor/generated/PTableProtos.java     | 104 +++++++++++++--
 .../apache/phoenix/jdbc/PhoenixConnection.java  |   6 +-
 .../phoenix/jdbc/PhoenixDatabaseMetaData.java   |   3 +
 .../query/ConnectionQueryServicesImpl.java      |  43 +++----
 .../query/ConnectionlessQueryServicesImpl.java  |   4 +-
 .../query/DelegateConnectionQueryServices.java  |   4 +-
 .../apache/phoenix/query/MetaDataMutated.java   |   2 +-
 .../apache/phoenix/query/QueryConstants.java    |   4 +-
 .../apache/phoenix/schema/DelegateTable.java    |   6 +
 .../apache/phoenix/schema/MetaDataClient.java   | 123 ++++++++++++------
 .../org/apache/phoenix/schema/PMetaData.java    |   3 +-
 .../apache/phoenix/schema/PMetaDataImpl.java    |  15 ++-
 .../java/org/apache/phoenix/schema/PTable.java  |   1 +
 .../org/apache/phoenix/schema/PTableImpl.java   |  65 ++++++----
 .../org/apache/phoenix/schema/PTableRef.java    |  17 ++-
 .../apache/phoenix/schema/TableProperty.java    |  26 ++++
 .../phoenix/execute/CorrelatePlanTest.java      |   4 +-
 .../phoenix/schema/PMetaDataImplTest.java       | 125 ++++++++++++-------
 phoenix-protocol/src/main/PTable.proto          |   1 +
 28 files changed, 487 insertions(+), 206 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
index 13ed8aa..20a3c48 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
@@ -49,8 +49,8 @@ import org.apache.phoenix.schema.types.PVarchar;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.TestUtil;
-import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -73,48 +73,86 @@ public class UpdateCacheIT extends BaseHBaseManagedTimeIT {
         setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
     }
     
-    @Before
-    public void setUp() throws SQLException {
-        ensureTableCreated(getUrl(), MUTABLE_INDEX_DATA_TABLE);
-        ensureTableCreated(getUrl(), TRANSACTIONAL_DATA_TABLE);
-    }
-
-    private static void setupSystemTable(Long scn) throws SQLException {
+    private static void setupSystemTable(String fullTableName) throws 
SQLException {
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        if (scn != null) {
-            props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, 
Long.toString(scn));
-        }
         try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
             conn.createStatement().execute(
-            "create table " + QueryConstants.SYSTEM_SCHEMA_NAME + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE + TEST_TABLE_SCHEMA);
+            "create table " + fullTableName + TEST_TABLE_SCHEMA);
         }
     }
     
     @Test
     public void testUpdateCacheForTxnTable() throws Exception {
-        helpTestUpdateCache(true, false, null);
+        String fullTableName = INDEX_DATA_SCHEMA + 
QueryConstants.NAME_SEPARATOR + TRANSACTIONAL_DATA_TABLE;
+        ensureTableCreated(getUrl(), TRANSACTIONAL_DATA_TABLE);
+        helpTestUpdateCache(fullTableName, null, new int[] {1, 1});
     }
     
     @Test
     public void testUpdateCacheForNonTxnTable() throws Exception {
-        helpTestUpdateCache(false, false, null);
+        String fullTableName = INDEX_DATA_SCHEMA + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+        ensureTableCreated(getUrl(), MUTABLE_INDEX_DATA_TABLE);
+        helpTestUpdateCache(fullTableName, null, new int[] {1, 3});
     }
        
     @Test
     public void testUpdateCacheForNonTxnSystemTable() throws Exception {
-        helpTestUpdateCache(false, true, null);
+        String fullTableName = QueryConstants.SYSTEM_SCHEMA_NAME + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+        setupSystemTable(fullTableName);
+        helpTestUpdateCache(fullTableName, null, new int[] {0, 0});
+    }
+    
+    @Test
+    public void testUpdateCacheForNeverUpdatedTable() throws Exception {
+        String fullTableName = INDEX_DATA_SCHEMA + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+        ensureTableCreated(getUrl(), MUTABLE_INDEX_DATA_TABLE);
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.createStatement().execute(
+            "alter table " + fullTableName + " SET 
UPDATE_CACHE_FREQUENCY=NEVER");
+        }
+        helpTestUpdateCache(fullTableName, null, new int[] {0, 0});
+    }
+    
+    @Test
+    public void testUpdateCacheForAlwaysUpdatedTable() throws Exception {
+        String fullTableName = INDEX_DATA_SCHEMA + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.createStatement().execute("CREATE TABLE " + fullTableName + 
TEST_TABLE_SCHEMA + " UPDATE_CACHE_FREQUENCY=always");
+        }
+        helpTestUpdateCache(fullTableName, null, new int[] {1, 3});
+    }
+    
+    @Test
+    public void testUpdateCacheForTimeLimitedUpdateTable() throws Exception {
+        String fullTableName = INDEX_DATA_SCHEMA + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.createStatement().execute("CREATE TABLE " + fullTableName + 
TEST_TABLE_SCHEMA + " UPDATE_CACHE_FREQUENCY=" + 10000);
+        }
+        helpTestUpdateCache(fullTableName, null, new int[] {0, 0});
+        Thread.sleep(10000);
+        helpTestUpdateCache(fullTableName, null, new int[] {1, 0});
+    }
+    
+    @Test
+    public void testUpdateCacheForChangingUpdateTable() throws Exception {
+        String fullTableName = INDEX_DATA_SCHEMA + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+        Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.createStatement().execute("CREATE TABLE " + fullTableName + 
TEST_TABLE_SCHEMA + " UPDATE_CACHE_FREQUENCY=never");
+        }
+        helpTestUpdateCache(fullTableName, null, new int[] {0, 0});
+        try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+            conn.createStatement().execute("ALTER TABLE " + fullTableName + " 
SET UPDATE_CACHE_FREQUENCY=ALWAYS");
+        }
+        helpTestUpdateCache(fullTableName, null, new int[] {1, 3});
     }
     
-       public static void helpTestUpdateCache(boolean isTransactional, boolean 
isSystem, Long scn) throws Exception {
-           String tableName = isTransactional ? TRANSACTIONAL_DATA_TABLE : 
MUTABLE_INDEX_DATA_TABLE;
-           String schemaName;
-           if (isSystem) {
-               setupSystemTable(scn);
-               schemaName = QueryConstants.SYSTEM_SCHEMA_NAME;
-           } else {
-               schemaName = INDEX_DATA_SCHEMA;
-           }
-           String fullTableName = schemaName + QueryConstants.NAME_SEPARATOR + 
tableName;
+       public static void helpTestUpdateCache(String fullTableName, Long scn, 
int[] expectedRPCs) throws Exception {
+           String tableName = 
SchemaUtil.getTableNameFromFullName(fullTableName);
+           String schemaName = 
SchemaUtil.getSchemaNameFromFullName(fullTableName);
                String selectSql = "SELECT * FROM "+fullTableName;
                // use a spyed ConnectionQueryServices so we can verify calls 
to getTable
                ConnectionQueryServices connectionQueryServices = 
Mockito.spy(driver.getConnectionQueryServices(getUrl(), 
PropertiesUtil.deepCopy(TEST_PROPERTIES)));
@@ -136,7 +174,7 @@ public class UpdateCacheIT extends BaseHBaseManagedTimeIT {
                        TestUtil.setRowKeyColumns(stmt, 3);
                        stmt.execute();
                        conn.commit();
-            int numUpsertRpcs = isSystem ? 0 : 1; 
+            int numUpsertRpcs = expectedRPCs[0];
                        // verify only 0 or 1 rpc to fetch table metadata, 
             verify(connectionQueryServices, 
times(numUpsertRpcs)).getTable((PName)isNull(), 
eq(PVarchar.INSTANCE.toBytes(schemaName)), 
eq(PVarchar.INSTANCE.toBytes(tableName)), anyLong(), anyLong());
             reset(connectionQueryServices);
@@ -169,7 +207,7 @@ public class UpdateCacheIT extends BaseHBaseManagedTimeIT {
             // for non-transactional tables with a scn : verify *only* one rpc 
occurs
             // for transactional tables : verify *only* one rpc occurs
                // for non-transactional, system tables : verify no rpc occurs
-            int numRpcs = isSystem ? 0 : (isTransactional || scn!=null ? 1 : 
3); 
+            int numRpcs = expectedRPCs[1]; 
             verify(connectionQueryServices, 
times(numRpcs)).getTable((PName)isNull(), 
eq(PVarchar.INSTANCE.toBytes(schemaName)), 
eq(PVarchar.INSTANCE.toBytes(tableName)), anyLong(), anyLong());
                }
         finally {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheWithScnIT.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheWithScnIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheWithScnIT.java
index 5ff2fb0..04f751b 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheWithScnIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheWithScnIT.java
@@ -17,25 +17,22 @@
  */
 package org.apache.phoenix.rpc;
 
+import static org.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA;
 import static org.apache.phoenix.util.TestUtil.MUTABLE_INDEX_DATA_TABLE;
 
 import org.apache.phoenix.end2end.BaseClientManagedTimeIT;
-import org.junit.Before;
+import org.apache.phoenix.query.QueryConstants;
 import org.junit.Test;
 
 public class UpdateCacheWithScnIT extends BaseClientManagedTimeIT {
        
-       protected long ts;
-
-       @Before
-       public void initTable() throws Exception {
-               ts = nextTimestamp();
-               ensureTableCreated(getUrl(), MUTABLE_INDEX_DATA_TABLE, ts);
-       }
-       
        @Test
        public void testUpdateCacheWithScn() throws Exception {
-               UpdateCacheIT.helpTestUpdateCache(false, false, ts+2);
+        long ts = nextTimestamp();
+        String fullTableName = INDEX_DATA_SCHEMA + 
QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+        ensureTableCreated(getUrl(), MUTABLE_INDEX_DATA_TABLE, ts);
+        // FIXME: given that the scn is advancing in the test, why aren't 
there more RPCs?
+               UpdateCacheIT.helpTestUpdateCache(fullTableName, ts+2, new 
int[] {1, 1});
        }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g 
b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index 18a153e..23f7e8f 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -509,7 +509,8 @@ fam_prop_name returns [PropertyName ret]
     ;
     
 prop_value returns [Object ret]
-    :   l=literal { $ret = l.getValue(); }
+    :   v=identifier { $ret = v; }
+    |   l=literal { $ret = l.getValue(); }
     ;
     
 column_name returns [ColumnName ret]

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
index 0828b94..9b2c460 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
@@ -652,7 +652,7 @@ public class FromCompiler {
                     PTableType.SUBQUERY, null, 
MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM,
                     null, null, columns, null, null, 
Collections.<PTable>emptyList(),
                     false, Collections.<PName>emptyList(), null, null, false, 
false, false, null,
-                    null, null, false, false);
+                    null, null, false, false, 0);
 
             String alias = subselectNode.getAlias();
             TableRef tableRef = new TableRef(alias, t, 
MetaDataProtocol.MIN_TABLE_TIMESTAMP, false);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
index b55e4aa..b64b9b7 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
@@ -1302,7 +1302,7 @@ public class JoinCompiler {
                 left.getBucketNum(), merged,left.getParentSchemaName(), 
left.getParentTableName(), left.getIndexes(),
                 left.isImmutableRows(), Collections.<PName>emptyList(), null, 
null, PTable.DEFAULT_DISABLE_WAL,
                 left.isMultiTenant(), left.getStoreNulls(), 
left.getViewType(), left.getViewIndexId(), left.getIndexType(),
-                left.rowKeyOrderOptimizable(), left.isTransactional());
+                left.rowKeyOrderOptimizable(), left.isTransactional(), 
left.getUpdateCacheFrequency());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
index 551b05c..0fc6d74 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
@@ -152,7 +152,7 @@ public class TupleProjectionCompiler {
                 table.getBucketNum(), projectedColumns, 
table.getParentSchemaName(),
                 table.getParentName(), table.getIndexes(), 
table.isImmutableRows(), Collections.<PName>emptyList(), null, null,
                 table.isWALDisabled(), table.isMultiTenant(), 
table.getStoreNulls(), table.getViewType(), table.getViewIndexId(),
-                table.getIndexType(), table.rowKeyOrderOptimizable(), 
table.isTransactional());
+                table.getIndexType(), table.rowKeyOrderOptimizable(), 
table.isTransactional(), table.getUpdateCacheFrequency());
     }
 
     public static PTable createProjectedTable(TableRef tableRef, 
List<ColumnRef> sourceColumnRefs, boolean retainPKColumns) throws SQLException {
@@ -179,7 +179,7 @@ public class TupleProjectionCompiler {
                     retainPKColumns ? table.getBucketNum() : null, 
projectedColumns, null,
                     null, Collections.<PTable>emptyList(), 
table.isImmutableRows(), Collections.<PName>emptyList(), null, null,
                     table.isWALDisabled(), table.isMultiTenant(), 
table.getStoreNulls(), table.getViewType(), table.getViewIndexId(),
-                    null, table.rowKeyOrderOptimizable(), 
table.isTransactional());
+                    null, table.rowKeyOrderOptimizable(), 
table.isTransactional(), table.getUpdateCacheFrequency());
     }
 
     // For extracting column references from single select statement

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
index 298303d..3bc1e37 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java
@@ -82,7 +82,7 @@ public class UnionCompiler {
         PTable tempTable = 
PTableImpl.makePTable(statement.getConnection().getTenantId(), 
UNION_SCHEMA_NAME, UNION_TABLE_NAME, 
                 PTableType.SUBQUERY, null, HConstants.LATEST_TIMESTAMP, scn == 
null ? HConstants.LATEST_TIMESTAMP : scn, null, null,
                         projectedColumns, null, null, null,
-                        true, null, null, null, true, true, true, null, null, 
null, false, false);
+                        true, null, null, null, true, true, true, null, null, 
null, false, false, 0);
         TableRef tableRef = new TableRef(null, tempTable, 0, false);
         return tableRef;
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
index 8c905ba..9887e7b 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
@@ -62,6 +62,7 @@ import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_TYPE_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TENANT_ID_INDEX;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TRANSACTIONAL_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_BYTES;
+import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.UPDATE_CACHE_FREQUENCY_BYTES;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_CONSTANT_BYTES;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID_BYTES;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_STATEMENT_BYTES;
@@ -242,6 +243,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
     private static final KeyValue BASE_COLUMN_COUNT_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
PhoenixDatabaseMetaData.BASE_COLUMN_COUNT_BYTES);
     private static final KeyValue ROW_KEY_ORDER_OPTIMIZABLE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
ROW_KEY_ORDER_OPTIMIZABLE_BYTES);
     private static final KeyValue TRANSACTIONAL_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TRANSACTIONAL_BYTES);
+    private static final KeyValue UPDATE_CACHE_FREQUENCY_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
UPDATE_CACHE_FREQUENCY_BYTES);
     
     private static final List<KeyValue> TABLE_KV_COLUMNS = 
Arrays.<KeyValue>asList(
             EMPTY_KEYVALUE_KV,
@@ -264,7 +266,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
             STORE_NULLS_KV,
             BASE_COLUMN_COUNT_KV,
             ROW_KEY_ORDER_OPTIMIZABLE_KV,
-            TRANSACTIONAL_KV
+            TRANSACTIONAL_KV,
+            UPDATE_CACHE_FREQUENCY_KV
             );
     static {
         Collections.sort(TABLE_KV_COLUMNS, KeyValue.COMPARATOR);
@@ -289,6 +292,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
     private static final int BASE_COLUMN_COUNT_INDEX = 
TABLE_KV_COLUMNS.indexOf(BASE_COLUMN_COUNT_KV);
     private static final int ROW_KEY_ORDER_OPTIMIZABLE_INDEX = 
TABLE_KV_COLUMNS.indexOf(ROW_KEY_ORDER_OPTIMIZABLE_KV);
     private static final int TRANSACTIONAL_INDEX = 
TABLE_KV_COLUMNS.indexOf(TRANSACTIONAL_KV);
+    private static final int UPDATE_CACHE_FREQUENCY_INDEX = 
TABLE_KV_COLUMNS.indexOf(UPDATE_CACHE_FREQUENCY_KV);
 
     // KeyValues for Column
     private static final KeyValue DECIMAL_DIGITS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DECIMAL_DIGITS_BYTES);
@@ -806,6 +810,10 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
             baseColumnCountKv.getValueOffset(), SortOrder.getDefault());
         Cell rowKeyOrderOptimizableKv = 
tableKeyValues[ROW_KEY_ORDER_OPTIMIZABLE_INDEX];
         boolean rowKeyOrderOptimizable = rowKeyOrderOptimizableKv == null ? 
false : 
Boolean.TRUE.equals(PBoolean.INSTANCE.toObject(rowKeyOrderOptimizableKv.getValueArray(),
 rowKeyOrderOptimizableKv.getValueOffset(), 
rowKeyOrderOptimizableKv.getValueLength()));
+        Cell updateCacheFrequencyKv = 
tableKeyValues[UPDATE_CACHE_FREQUENCY_INDEX];
+        long updateCacheFrequency = updateCacheFrequencyKv == null ? 0 :
+            
PLong.INSTANCE.getCodec().decodeLong(updateCacheFrequencyKv.getValueArray(),
+                    updateCacheFrequencyKv.getValueOffset(), 
SortOrder.getDefault());
 
         List<PColumn> columns = 
Lists.newArrayListWithExpectedSize(columnCount);
         List<PTable> indexes = new ArrayList<PTable>();
@@ -850,7 +858,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         return PTableImpl.makePTable(tenantId, schemaName, tableName, 
tableType, indexState, timeStamp,
             tableSeqNum, pkName, saltBucketNum, columns, tableType == INDEX ? 
schemaName : null,
             tableType == INDEX ? dataTableName : null, indexes, 
isImmutableRows, physicalTables, defaultFamilyName, viewStatement,
-            disableWAL, multiTenant, storeNulls, viewType, viewIndexId, 
indexType, stats, baseColumnCount, rowKeyOrderOptimizable, transactional);
+            disableWAL, multiTenant, storeNulls, viewType, viewIndexId, 
indexType, rowKeyOrderOptimizable, transactional, updateCacheFrequency,
+            stats, baseColumnCount);
     }
 
     private PFunction getFunction(RegionScanner scanner, final boolean 
isReplace, long clientTimeStamp, List<Mutation> deleteMutationsForReplace)

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
index b7b936e..a704e22 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
@@ -73,7 +73,7 @@ public abstract class MetaDataProtocol extends 
MetaDataService {
     public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_3_0 = 
MIN_TABLE_TIMESTAMP + 7;
     public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_5_0 = 
MIN_TABLE_TIMESTAMP + 8;
     public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_6_0 = 
MIN_TABLE_TIMESTAMP + 9;
-    public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0 = 
MIN_TABLE_TIMESTAMP + 11;
+    public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0 = 
MIN_TABLE_TIMESTAMP + 13;
     // MIN_SYSTEM_TABLE_TIMESTAMP needs to be set to the max of all the 
MIN_SYSTEM_TABLE_TIMESTAMP_* constants
     public static final long MIN_SYSTEM_TABLE_TIMESTAMP = 
MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0;
     // TODO: pare this down to minimum, as we don't need duplicates for both 
table and column errors, nor should we need

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
index 7e71cd9..be8d7e2 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
@@ -3228,6 +3228,16 @@ public final class PTableProtos {
      * <code>optional bool transactional = 27;</code>
      */
     boolean getTransactional();
+
+    // optional int64 updateCacheFrequency = 28;
+    /**
+     * <code>optional int64 updateCacheFrequency = 28;</code>
+     */
+    boolean hasUpdateCacheFrequency();
+    /**
+     * <code>optional int64 updateCacheFrequency = 28;</code>
+     */
+    long getUpdateCacheFrequency();
   }
   /**
    * Protobuf type {@code PTable}
@@ -3433,6 +3443,11 @@ public final class PTableProtos {
               transactional_ = input.readBool();
               break;
             }
+            case 224: {
+              bitField0_ |= 0x00800000;
+              updateCacheFrequency_ = input.readInt64();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -4011,6 +4026,22 @@ public final class PTableProtos {
       return transactional_;
     }
 
+    // optional int64 updateCacheFrequency = 28;
+    public static final int UPDATECACHEFREQUENCY_FIELD_NUMBER = 28;
+    private long updateCacheFrequency_;
+    /**
+     * <code>optional int64 updateCacheFrequency = 28;</code>
+     */
+    public boolean hasUpdateCacheFrequency() {
+      return ((bitField0_ & 0x00800000) == 0x00800000);
+    }
+    /**
+     * <code>optional int64 updateCacheFrequency = 28;</code>
+     */
+    public long getUpdateCacheFrequency() {
+      return updateCacheFrequency_;
+    }
+
     private void initFields() {
       schemaNameBytes_ = com.google.protobuf.ByteString.EMPTY;
       tableNameBytes_ = com.google.protobuf.ByteString.EMPTY;
@@ -4039,6 +4070,7 @@ public final class PTableProtos {
       baseColumnCount_ = 0;
       rowKeyOrderOptimizable_ = false;
       transactional_ = false;
+      updateCacheFrequency_ = 0L;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -4187,6 +4219,9 @@ public final class PTableProtos {
       if (((bitField0_ & 0x00400000) == 0x00400000)) {
         output.writeBool(27, transactional_);
       }
+      if (((bitField0_ & 0x00800000) == 0x00800000)) {
+        output.writeInt64(28, updateCacheFrequency_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -4309,6 +4344,10 @@ public final class PTableProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeBoolSize(27, transactional_);
       }
+      if (((bitField0_ & 0x00800000) == 0x00800000)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(28, updateCacheFrequency_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -4455,6 +4494,11 @@ public final class PTableProtos {
         result = result && (getTransactional()
             == other.getTransactional());
       }
+      result = result && (hasUpdateCacheFrequency() == 
other.hasUpdateCacheFrequency());
+      if (hasUpdateCacheFrequency()) {
+        result = result && (getUpdateCacheFrequency()
+            == other.getUpdateCacheFrequency());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -4576,6 +4620,10 @@ public final class PTableProtos {
         hash = (37 * hash) + TRANSACTIONAL_FIELD_NUMBER;
         hash = (53 * hash) + hashBoolean(getTransactional());
       }
+      if (hasUpdateCacheFrequency()) {
+        hash = (37 * hash) + UPDATECACHEFREQUENCY_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getUpdateCacheFrequency());
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -4754,6 +4802,8 @@ public final class PTableProtos {
         bitField0_ = (bitField0_ & ~0x02000000);
         transactional_ = false;
         bitField0_ = (bitField0_ & ~0x04000000);
+        updateCacheFrequency_ = 0L;
+        bitField0_ = (bitField0_ & ~0x08000000);
         return this;
       }
 
@@ -4906,6 +4956,10 @@ public final class PTableProtos {
           to_bitField0_ |= 0x00400000;
         }
         result.transactional_ = transactional_;
+        if (((from_bitField0_ & 0x08000000) == 0x08000000)) {
+          to_bitField0_ |= 0x00800000;
+        }
+        result.updateCacheFrequency_ = updateCacheFrequency_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -5081,6 +5135,9 @@ public final class PTableProtos {
         if (other.hasTransactional()) {
           setTransactional(other.getTransactional());
         }
+        if (other.hasUpdateCacheFrequency()) {
+          setUpdateCacheFrequency(other.getUpdateCacheFrequency());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -6784,6 +6841,39 @@ public final class PTableProtos {
         return this;
       }
 
+      // optional int64 updateCacheFrequency = 28;
+      private long updateCacheFrequency_ ;
+      /**
+       * <code>optional int64 updateCacheFrequency = 28;</code>
+       */
+      public boolean hasUpdateCacheFrequency() {
+        return ((bitField0_ & 0x08000000) == 0x08000000);
+      }
+      /**
+       * <code>optional int64 updateCacheFrequency = 28;</code>
+       */
+      public long getUpdateCacheFrequency() {
+        return updateCacheFrequency_;
+      }
+      /**
+       * <code>optional int64 updateCacheFrequency = 28;</code>
+       */
+      public Builder setUpdateCacheFrequency(long value) {
+        bitField0_ |= 0x08000000;
+        updateCacheFrequency_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int64 updateCacheFrequency = 28;</code>
+       */
+      public Builder clearUpdateCacheFrequency() {
+        bitField0_ = (bitField0_ & ~0x08000000);
+        updateCacheFrequency_ = 0L;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:PTable)
     }
 
@@ -6830,7 +6920,7 @@ public final class PTableProtos {
       "leStats\022\013\n\003key\030\001 \002(\014\022\016\n\006values\030\002 
\003(\014\022\033\n\023" +
       "guidePostsByteCount\030\003 \001(\003\022\025\n\rkeyBytesCou",
       "nt\030\004 \001(\003\022\027\n\017guidePostsCount\030\005 
\001(\005\022!\n\013pGu" +
-      "idePosts\030\006 \001(\0132\014.PGuidePosts\"\206\005\n\006PTable\022" +
+      "idePosts\030\006 \001(\0132\014.PGuidePosts\"\244\005\n\006PTable\022" +
       "\027\n\017schemaNameBytes\030\001 \002(\014\022\026\n\016tableNameByt" +
       "es\030\002 \002(\014\022\036\n\ttableType\030\003 
\002(\0162\013.PTableType" +
       "\022\022\n\nindexState\030\004 
\001(\t\022\026\n\016sequenceNumber\030\005" +
@@ -6846,11 +6936,11 @@ public final class PTableProtos {
       "wIndexId\030\025 \001(\005\022\021\n\tindexType\030\026 
\001(\014\022\026\n\016sta" +
       "tsTimeStamp\030\027 \001(\003\022\022\n\nstoreNulls\030\030 
\001(\010\022\027\n" +
       "\017baseColumnCount\030\031 \001(\005\022\036\n\026rowKeyOrderOpt" +
-      "imizable\030\032 \001(\010\022\025\n\rtransactional\030\033 
\001(\010*A\n" +
-      
"\nPTableType\022\n\n\006SYSTEM\020\000\022\010\n\004USER\020\001\022\010\n\004VIE"
 +
-      
"W\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020\004B@\n(org.apache.p",
-      "hoenix.coprocessor.generatedB\014PTableProt" +
-      "osH\001\210\001\001\240\001\001"
+      "imizable\030\032 \001(\010\022\025\n\rtransactional\030\033 
\001(\010\022\034\n" +
+      "\024updateCacheFrequency\030\034 \001(\003*A\n\nPTableTyp" +
+      
"e\022\n\n\006SYSTEM\020\000\022\010\n\004USER\020\001\022\010\n\004VIEW\020\002\022\t\n\005IND",
+      "EX\020\003\022\010\n\004JOIN\020\004B@\n(org.apache.phoenix.cop" +
+      "rocessor.generatedB\014PTableProtosH\001\210\001\001\240\001\001"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner 
assigner =
       new 
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -6874,7 +6964,7 @@ public final class PTableProtos {
           internal_static_PTable_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_PTable_descriptor,
-              new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", 
"TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", 
"BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", 
"DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", 
"ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", 
"IndexType", "StatsTimeStamp", "StoreNulls", "BaseColumnCount", 
"RowKeyOrderOptimizable", "Transactional", });
+              new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", 
"TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", 
"BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", 
"DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", 
"ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", 
"IndexType", "StatsTimeStamp", "StoreNulls", "BaseColumnCount", 
"RowKeyOrderOptimizable", "Transactional", "UpdateCacheFrequency", });
           return null;
         }
       };

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
index 208e874..82bf31a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
@@ -913,11 +913,11 @@ public class PhoenixConnection implements Connection, 
MetaDataMutated, SQLClosea
     }
 
     @Override
-    public PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columns, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows, 
boolean isWalDisabled, boolean isMultitenant, boolean storeNulls, boolean 
isTransactional, long resolvedTime)
+    public PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columns, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows, 
boolean isWalDisabled, boolean isMultitenant, boolean storeNulls, boolean 
isTransactional, long updateCacheFrequency, long resolvedTime)
             throws SQLException {
-        metaData = metaData.addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, resolvedTime);
+        metaData = metaData.addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, updateCacheFrequency, resolvedTime);
         //Cascade through to connectionQueryServices too
-        getQueryServices().addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, resolvedTime);
+        getQueryServices().addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, updateCacheFrequency, resolvedTime);
         return metaData;
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
index 869ba19..fabd949 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
@@ -285,6 +285,9 @@ public class PhoenixDatabaseMetaData implements 
DatabaseMetaData {
     public static final String TRANSACTIONAL = "TRANSACTIONAL";
     public static final byte[] TRANSACTIONAL_BYTES = 
Bytes.toBytes(TRANSACTIONAL);
 
+    public static final String UPDATE_CACHE_FREQUENCY = 
"UPDATE_CACHE_FREQUENCY";
+    public static final byte[] UPDATE_CACHE_FREQUENCY_BYTES = 
Bytes.toBytes(UPDATE_CACHE_FREQUENCY);
+
     public static final String ASYNC_CREATED_DATE = "ASYNC_CREATED_DATE";
 
     private final PhoenixConnection connection;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 8ef3161..a246e63 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -630,12 +630,14 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
      }
 
     @Override
-    public PMetaData addColumn(final PName tenantId, final String tableName, 
final List<PColumn> columns, final long tableTimeStamp, final long tableSeqNum, 
final boolean isImmutableRows, final boolean isWalDisabled, final boolean 
isMultitenant, final boolean storeNulls, final boolean isTransactional, final 
long resolvedTime) throws SQLException {
+    public PMetaData addColumn(final PName tenantId, final String tableName, 
final List<PColumn> columns, final long tableTimeStamp, 
+            final long tableSeqNum, final boolean isImmutableRows, final 
boolean isWalDisabled, final boolean isMultitenant, 
+            final boolean storeNulls, final boolean isTransactional, final 
long updateCacheFrequency, final long resolvedTime) throws SQLException {
         return metaDataMutated(tenantId, tableName, tableSeqNum, new Mutator() 
{
             @Override
             public PMetaData mutate(PMetaData metaData) throws SQLException {
                 try {
-                    return metaData.addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, resolvedTime);
+                    return metaData.addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, updateCacheFrequency, resolvedTime);
                 } catch (TableNotFoundException e) {
                     // The DROP TABLE may have been processed first, so just 
ignore.
                     return metaData;
@@ -2410,17 +2412,20 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
                                 }
                                 if (currentServerSideTableTimeStamp < 
MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_6_0) {
                                     columnsToAdd = 
PhoenixDatabaseMetaData.IS_ROW_TIMESTAMP + " " + 
PBoolean.INSTANCE.getSqlTypeName();
-                                    metaConnection = addColumn(metaConnection, 
PhoenixDatabaseMetaData.SYSTEM_CATALOG,
-                                            
MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_6_0, columnsToAdd, false);
+                                    metaConnection = 
addColumnsIfNotExists(metaConnection, PhoenixDatabaseMetaData.SYSTEM_CATALOG,
+                                            
MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_6_0, columnsToAdd);
                                 }
                                 if(currentServerSideTableTimeStamp < 
MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0) {
-                                    columnsToAdd = 
PhoenixDatabaseMetaData.TRANSACTIONAL + " " + 
PBoolean.INSTANCE.getSqlTypeName();
-                                    metaConnection = addColumn(metaConnection, 
PhoenixDatabaseMetaData.SYSTEM_CATALOG,
-                                            
MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0, columnsToAdd, false);
-                                                                       // Drop 
old stats table so that new stats
-                                                                       // table
+                                    // Add these columns one at a time, each 
with different timestamps so that if folks have
+                                    // run the upgrade code already for a 
snapshot, we'll still enter this block (and do the
+                                    // parts we haven't yet done).
+                                    metaConnection = 
addColumnsIfNotExists(metaConnection, PhoenixDatabaseMetaData.SYSTEM_CATALOG, 
MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0 - 2,
+                                            
PhoenixDatabaseMetaData.TRANSACTIONAL + " " + 
PBoolean.INSTANCE.getSqlTypeName());
+                                    metaConnection = 
addColumnsIfNotExists(metaConnection, PhoenixDatabaseMetaData.SYSTEM_CATALOG, 
MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0 - 1, 
+                                            
PhoenixDatabaseMetaData.UPDATE_CACHE_FREQUENCY + " " + 
PLong.INSTANCE.getSqlTypeName());
+                                                                       // Drop 
old stats table so that new stats table is created
                                                                        
metaConnection = dropStatsTable(metaConnection,
-                                                                               
        MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0 - 1);
+                                                                               
        MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0);
                                 }
                                 
                             }
@@ -2525,7 +2530,7 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
        private PhoenixConnection dropStatsTable(PhoenixConnection 
oldMetaConnection, long timestamp)
                        throws SQLException, IOException {
                Properties props = 
PropertiesUtil.deepCopy(oldMetaConnection.getClientInfo());
-               props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, 
Long.toString(timestamp));
+               props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, 
Long.toString(timestamp-1));
                PhoenixConnection metaConnection = new 
PhoenixConnection(oldMetaConnection, this, props);
                SQLException sqlE = null;
                boolean wasCommit = metaConnection.getAutoCommit();
@@ -2555,23 +2560,9 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
                        }
                }
 
-               HBaseAdmin admin = null;
-               try {
-                       admin = getAdmin();
-                       
admin.disableTable(PhoenixDatabaseMetaData.SYSTEM_STATS_NAME_BYTES);
-                       try {
-                               
admin.deleteTable(PhoenixDatabaseMetaData.SYSTEM_STATS_NAME_BYTES);
-                       } catch (org.apache.hadoop.hbase.TableNotFoundException 
e) {
-                               logger.debug("Stats table was not found during 
upgrade!!");
-                       }
-               } finally {
-                       if (admin != null)
-                               admin.close();
-               }
                oldMetaConnection = metaConnection;
                props = 
PropertiesUtil.deepCopy(oldMetaConnection.getClientInfo());
-               props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB,
-                               
Long.toString(MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP_4_7_0));
+               props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, 
Long.toString(timestamp));
                try {
                        metaConnection = new 
PhoenixConnection(oldMetaConnection, ConnectionQueryServicesImpl.this, props);
                } finally {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
index 199b010..6cfb382 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
@@ -177,8 +177,8 @@ public class ConnectionlessQueryServicesImpl extends 
DelegateQueryServices imple
 
     @Override
     public PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columns, long tableTimeStamp,
-            long tableSeqNum, boolean isImmutableRows, boolean isWalDisabled, 
boolean isMultitenant, boolean storeNulls, boolean isTransactional, long 
resolvedTime) throws SQLException {
-        return metaData = metaData.addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, resolvedTime);
+            long tableSeqNum, boolean isImmutableRows, boolean isWalDisabled, 
boolean isMultitenant, boolean storeNulls, boolean isTransactional, long 
updateCacheFrequency, long resolvedTime) throws SQLException {
+        return metaData = metaData.addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, updateCacheFrequency, resolvedTime);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
index 84f3e74..9b721f8 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
@@ -87,8 +87,8 @@ public class DelegateConnectionQueryServices extends 
DelegateQueryServices imple
 
     @Override
     public PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columns, long tableTimeStamp,
-            long tableSeqNum, boolean isImmutableRows, boolean isWalDisabled, 
boolean isMultitenant, boolean storeNulls, boolean isTransactional, long 
resolvedTime) throws SQLException {
-        return getDelegate().addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, resolvedTime);
+            long tableSeqNum, boolean isImmutableRows, boolean isWalDisabled, 
boolean isMultitenant, boolean storeNulls, boolean isTransactional, long 
updateCacheFrequency, long resolvedTime) throws SQLException {
+        return getDelegate().addColumn(tenantId, tableName, columns, 
tableTimeStamp, tableSeqNum, isImmutableRows, isWalDisabled, isMultitenant, 
storeNulls, isTransactional, updateCacheFrequency, resolvedTime);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java
index 8e7a70d..753b172 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/MetaDataMutated.java
@@ -38,7 +38,7 @@ public interface MetaDataMutated {
     PMetaData addTable(PTable table, long resolvedTime) throws SQLException;
     PMetaData updateResolvedTimestamp(PTable table, long resolvedTimestamp) 
throws SQLException;
     PMetaData removeTable(PName tenantId, String tableName, String 
parentTableName, long tableTimeStamp) throws SQLException;
-    PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columns, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows, 
boolean isWalDisabled, boolean isMultitenant, boolean storeNulls, boolean 
isTransactional, long resolvedTime) throws SQLException;
+    PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columns, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows, 
boolean isWalDisabled, boolean isMultitenant, boolean storeNulls, boolean 
isTransactional, long updateCacheFrequency, long resolvedTime) throws 
SQLException;
     PMetaData removeColumn(PName tenantId, String tableName, List<PColumn> 
columnsToRemove, long tableTimeStamp, long tableSeqNum, long resolvedTime) 
throws SQLException;
     PMetaData addFunction(PFunction function) throws SQLException;
     PMetaData removeFunction(PName tenantId, String function, long 
functionTimeStamp) throws SQLException;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
index c1cb0c0..63d4e07 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
@@ -39,9 +39,9 @@ import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAM
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DEFAULT_VALUE;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.DISABLE_WAL;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.FUNCTION_NAME;
+import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POSTS_WIDTH;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POST_KEY;
-import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.GUIDE_POSTS_ROW_COUNT;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.IMMUTABLE_ROWS;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.INCREMENT_BY;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.INDEX_DISABLE_TIMESTAMP;
@@ -96,6 +96,7 @@ import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TRANSACTIONAL;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_NAME;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE_SEQUENCE;
+import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.UPDATE_CACHE_FREQUENCY;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_CONSTANT;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_STATEMENT;
@@ -257,6 +258,7 @@ public interface QueryConstants {
             // Column metadata (will be null for table row)
             IS_ROW_TIMESTAMP + " BOOLEAN, " +
             TRANSACTIONAL + " BOOLEAN," +
+            UPDATE_CACHE_FREQUENCY + " BIGINT," +
             "CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY (" + 
TENANT_ID + ","
             + TABLE_SCHEM + "," + TABLE_NAME + "," + COLUMN_NAME + "," + 
COLUMN_FAMILY + "))\n" +
             HConstants.VERSIONS + "=" + 
MetaDataProtocol.DEFAULT_MAX_META_DATA_VERSIONS + ",\n" +

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
index 7fb90a1..e7bf961 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java
@@ -242,6 +242,7 @@ public class DelegateTable implements PTable {
         return delegate.isTransactional();
     }
 
+    @Override
     public int getBaseColumnCount() {
         return delegate.getBaseColumnCount();
     }
@@ -260,4 +261,9 @@ public class DelegateTable implements PTable {
     public String toString() {
         return delegate.toString();
     }
+
+    @Override
+    public long getUpdateCacheFrequency() {
+        return delegate.getUpdateCacheFrequency();
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index ee8fba3..ee212ed 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -76,6 +76,7 @@ import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_TYPE;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TENANT_ID;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TRANSACTIONAL;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TYPE;
+import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.UPDATE_CACHE_FREQUENCY;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_CONSTANT;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_STATEMENT;
@@ -250,8 +251,9 @@ public class MetaDataClient {
             INDEX_TYPE + "," +
             STORE_NULLS + "," +
             BASE_COLUMN_COUNT + "," +
-            TRANSACTIONAL +
-            ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 
?, ?, ?)";
+            TRANSACTIONAL + "," +
+            UPDATE_CACHE_FREQUENCY +
+            ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 
?, ?, ?, ?)";
     private static final String CREATE_LINK =
             "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + 
SYSTEM_CATALOG_TABLE + "\"( " +
             TENANT_ID + "," +
@@ -477,8 +479,8 @@ public class MetaDataClient {
         // Do not make rpc to getTable if 
         // 1. table is a system table
         // 2. table was already resolved as of that timestamp
-        if (table != null && !alwaysHitServer
-                && (systemTable || resolvedTimestamp == 
tableResolvedTimestamp)) {
+               if (table != null && !alwaysHitServer
+                               && (systemTable || resolvedTimestamp == 
tableResolvedTimestamp || connection.getMetaDataCache().getAge(tableRef) < 
table.getUpdateCacheFrequency())) {
             return new 
MetaDataMutationResult(MutationCode.TABLE_ALREADY_EXISTS, 
QueryConstants.UNSET_TIMESTAMP, table);
         }
 
@@ -1649,7 +1651,7 @@ public class MetaDataClient {
             // Although unusual, it's possible to set a mapped VIEW as having 
immutable rows.
             // This tells Phoenix that you're managing the index maintenance 
yourself.
             if (tableType != PTableType.INDEX && (tableType != PTableType.VIEW 
|| viewType == ViewType.MAPPED)) {
-                Boolean isImmutableRowsProp = (Boolean) 
tableProps.get(PTable.IS_IMMUTABLE_ROWS_PROP_NAME);
+                Boolean isImmutableRowsProp = (Boolean) 
TableProperty.IMMUTABLE_ROWS.getValue(tableProps);
                 if (isImmutableRowsProp == null) {
                     isImmutableRows = 
connection.getQueryServices().getProps().getBoolean(QueryServices.IMMUTABLE_ROWS_ATTRIB,
 QueryServicesOptions.DEFAULT_IMMUTABLE_ROWS);
                 } else {
@@ -1659,7 +1661,7 @@ public class MetaDataClient {
 
             // Can't set any of these on views or shared indexes on views
             if (tableType != PTableType.VIEW && indexId == null) {
-                saltBucketNum = (Integer) 
tableProps.get(PhoenixDatabaseMetaData.SALT_BUCKETS);
+                saltBucketNum = (Integer) 
TableProperty.SALT_BUCKETS.getValue(tableProps);
                 if (saltBucketNum != null) {
                     if (saltBucketNum < 0 || saltBucketNum > 
SaltingUtil.MAX_BUCKET_NUM) {
                         throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_BUCKET_NUM).build().buildException();
@@ -1680,16 +1682,21 @@ public class MetaDataClient {
             if (tableType != PTableType.INDEX && (tableType != PTableType.VIEW 
|| viewType == ViewType.MAPPED)) {
                 Boolean multiTenantProp = (Boolean) 
tableProps.get(PhoenixDatabaseMetaData.MULTI_TENANT);
                 multiTenant = Boolean.TRUE.equals(multiTenantProp);
-                defaultFamilyName = 
(String)tableProps.get(PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME);
+                defaultFamilyName = 
(String)TableProperty.DEFAULT_COLUMN_FAMILY.getValue(tableProps);
             }
 
             boolean disableWAL = false;
-            Boolean disableWALProp = (Boolean) 
tableProps.get(PhoenixDatabaseMetaData.DISABLE_WAL);
+            Boolean disableWALProp = (Boolean) 
TableProperty.DISABLE_WAL.getValue(tableProps);
             if (disableWALProp != null) {
                 disableWAL = disableWALProp;
             }
+            long updateCacheFrequency = 0;
+            Long updateCacheFrequencyProp = (Long) 
TableProperty.UPDATE_CACHE_FREQUENCY.getValue(tableProps);
+            if (updateCacheFrequencyProp != null) {
+                updateCacheFrequency = updateCacheFrequencyProp;
+            }
 
-            Boolean storeNullsProp = (Boolean) 
tableProps.get(PhoenixDatabaseMetaData.STORE_NULLS);
+            Boolean storeNullsProp = (Boolean) 
TableProperty.STORE_NULLS.getValue(tableProps);
             if (storeNullsProp == null) {
                 if (parent == null) {
                     storeNulls = 
connection.getQueryServices().getProps().getBoolean(
@@ -1700,7 +1707,7 @@ public class MetaDataClient {
             } else {
                 storeNulls = storeNullsProp;
             }
-            Boolean transactionalProp = (Boolean) 
tableProps.get(PhoenixDatabaseMetaData.TRANSACTIONAL);
+            Boolean transactionalProp = (Boolean) 
TableProperty.TRANSACTIONAL.getValue(tableProps);
             if (transactionalProp != null && parent != null) {
                 throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.ONLY_TABLE_MAY_BE_DECLARED_TRANSACTIONAL)
                 .setSchemaName(schemaName).setTableName(tableName)
@@ -1731,7 +1738,8 @@ public class MetaDataClient {
                 .build().buildException();
             }
             
-            
+            // Put potentially inferred value into tableProps as it's used by 
the createTable call below
+            // to determine which coprocessors to install on the new table.
             tableProps.put(PhoenixDatabaseMetaData.TRANSACTIONAL, 
transactional);
             if (transactional) {
                 // If TTL set, use Tephra TTL property name instead
@@ -2020,7 +2028,7 @@ public class MetaDataClient {
                         Collections.<PTable>emptyList(), isImmutableRows,
                         Collections.<PName>emptyList(), defaultFamilyName == 
null ? null :
                                 PNameFactory.newName(defaultFamilyName), null,
-                        Boolean.TRUE.equals(disableWAL), false, false, null, 
indexId, indexType, true, false);
+                        Boolean.TRUE.equals(disableWAL), false, false, null, 
indexId, indexType, true, false, 0);
                 connection.addTable(table, 
MetaDataProtocol.MIN_TABLE_TIMESTAMP);
             } else if (tableType == PTableType.INDEX && indexId == null) {
                 if (tableProps.get(HTableDescriptor.MAX_FILESIZE) == null) {
@@ -2123,6 +2131,7 @@ public class MetaDataClient {
                 tableUpsert.setInt(20, BASE_TABLE_BASE_COLUMN_COUNT);
             }
             tableUpsert.setBoolean(21, transactional);
+            tableUpsert.setLong(22, updateCacheFrequency);
             tableUpsert.execute();
 
             if (asyncCreatedDate != null) {
@@ -2185,7 +2194,7 @@ public class MetaDataClient {
                         PTable.INITIAL_SEQ_NUM, pkName == null ? null : 
PNameFactory.newName(pkName), saltBucketNum, columns,
                         dataTableName == null ? null : newSchemaName, 
dataTableName == null ? null : PNameFactory.newName(dataTableName), 
Collections.<PTable>emptyList(), isImmutableRows,
                         physicalNames, defaultFamilyName == null ? null : 
PNameFactory.newName(defaultFamilyName), viewStatement, 
Boolean.TRUE.equals(disableWAL), multiTenant, storeNulls, viewType,
-                        indexId, indexType, rowKeyOrderOptimizable, 
transactional);
+                        indexId, indexType, rowKeyOrderOptimizable, 
transactional, updateCacheFrequency);
                 result = new MetaDataMutationResult(code, 
result.getMutationTime(), table, true);
                 addTableToCache(result);
                 return table;
@@ -2508,12 +2517,12 @@ public class MetaDataClient {
         return mutationCode;
     }
 
-    private  long incrementTableSeqNum(PTable table, PTableType expectedType, 
int columnCountDelta, Boolean isTransactional) throws SQLException {
-        return incrementTableSeqNum(table, expectedType, columnCountDelta, 
isTransactional, null, null, null, null);
+    private  long incrementTableSeqNum(PTable table, PTableType expectedType, 
int columnCountDelta, Boolean isTransactional, Long updateCacheFrequency) 
throws SQLException {
+        return incrementTableSeqNum(table, expectedType, columnCountDelta, 
isTransactional, updateCacheFrequency, null, null, null, null);
     }
 
     private long incrementTableSeqNum(PTable table, PTableType expectedType, 
int columnCountDelta,
-            Boolean isTransactional, Boolean isImmutableRows, Boolean 
disableWAL, Boolean isMultiTenant, Boolean storeNulls)
+            Boolean isTransactional, Long updateCacheFrequency, Boolean 
isImmutableRows, Boolean disableWAL, Boolean isMultiTenant, Boolean storeNulls)
             throws SQLException {
         String schemaName = table.getSchemaName().getString();
         String tableName = table.getTableName().getString();
@@ -2548,6 +2557,9 @@ public class MetaDataClient {
         if (isTransactional != null) {
             mutateBooleanProperty(tenantId, schemaName, tableName, 
TRANSACTIONAL, isTransactional);
         }
+        if (updateCacheFrequency != null) {
+            mutateLongProperty(tenantId, schemaName, tableName, 
UPDATE_CACHE_FREQUENCY, updateCacheFrequency);
+        }
         return seqNum;
     }
 
@@ -2567,6 +2579,22 @@ public class MetaDataClient {
         tableBoolUpsert.execute();
     }
 
+    private void mutateLongProperty(String tenantId, String schemaName, String 
tableName,
+            String propertyName, long propertyValue) throws SQLException {
+        String updatePropertySql = "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + 
".\"" + SYSTEM_CATALOG_TABLE + "\"( " +
+                        TENANT_ID + "," +
+                        TABLE_SCHEM + "," +
+                        TABLE_NAME + "," +
+                        propertyName +
+                        ") VALUES (?, ?, ?, ?)";
+        PreparedStatement tableBoolUpsert = 
connection.prepareStatement(updatePropertySql);
+        tableBoolUpsert.setString(1, tenantId);
+        tableBoolUpsert.setString(2, schemaName);
+        tableBoolUpsert.setString(3, tableName);
+        tableBoolUpsert.setLong(4, propertyValue);
+        tableBoolUpsert.execute();
+    }
+
     public MutationState addColumn(AddColumnStatement statement) throws 
SQLException {
         connection.rollback();
         boolean wasAutoCommit = connection.getAutoCommit();
@@ -2582,6 +2610,7 @@ public class MetaDataClient {
             Boolean disableWALProp = null;
             Boolean storeNullsProp = null;
             Boolean isTransactionalProp = null;
+            Long updateCacheFrequencyProp = null;
 
             ListMultimap<String,Pair<String,Object>> stmtProperties = 
statement.getProps();
             Map<String, List<Pair<String, Object>>> properties = new 
HashMap<>(stmtProperties.size());
@@ -2596,17 +2625,21 @@ public class MetaDataClient {
                 for (Pair<String, Object> prop : propsList) {
                     String propName = prop.getFirst();
                     if (TableProperty.isPhoenixTableProperty(propName)) {
-                        TableProperty.valueOf(propName).validate(true, 
!family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY), table.getType());
+                        TableProperty tableProp = 
TableProperty.valueOf(propName);
+                        tableProp.validate(true, 
!family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY), table.getType());
+                        Object value = tableProp.getValue(prop.getSecond());
                         if 
(propName.equals(PTable.IS_IMMUTABLE_ROWS_PROP_NAME)) {
-                            isImmutableRowsProp = (Boolean)prop.getSecond();
+                            isImmutableRowsProp = (Boolean)value;
                         } else if 
(propName.equals(PhoenixDatabaseMetaData.MULTI_TENANT)) {
-                            multiTenantProp = (Boolean)prop.getSecond();
+                            multiTenantProp = (Boolean)value;
                         } else if (propName.equals(DISABLE_WAL)) {
-                            disableWALProp = (Boolean)prop.getSecond();
+                            disableWALProp = (Boolean)value;
                         } else if (propName.equals(STORE_NULLS)) {
-                            storeNullsProp = (Boolean)prop.getSecond();
+                            storeNullsProp = (Boolean)value;
                         } else if (propName.equals(TRANSACTIONAL)) {
-                            isTransactionalProp = (Boolean)prop.getSecond();
+                            isTransactionalProp = (Boolean)value;
+                        } else if (propName.equals(UPDATE_CACHE_FREQUENCY)) {
+                            updateCacheFrequencyProp = (Long)value;
                         }
                     } 
                 }
@@ -2662,6 +2695,13 @@ public class MetaDataClient {
                         changingPhoenixTableProperty = true;
                     }
                 }
+                Long updateCacheFrequency = null;
+                if (updateCacheFrequencyProp != null) {
+                    if (updateCacheFrequencyProp.longValue() != 
table.getUpdateCacheFrequency()) {
+                        updateCacheFrequency = updateCacheFrequencyProp;
+                        changingPhoenixTableProperty = true;
+                    }
+                }
                 Boolean storeNulls = null;
                 if (storeNullsProp != null) {
                     if (storeNullsProp.booleanValue() != 
table.getStoreNulls()) {
@@ -2795,14 +2835,14 @@ public class MetaDataClient {
 
                 if (!table.getIndexes().isEmpty() && (numPkColumnsAdded>0 || 
nonTxToTx)) {
                     for (PTable index : table.getIndexes()) {
-                        incrementTableSeqNum(index, index.getType(), 
numPkColumnsAdded, nonTxToTx ? Boolean.TRUE : null);
+                        incrementTableSeqNum(index, index.getType(), 
numPkColumnsAdded, nonTxToTx ? Boolean.TRUE : null, updateCacheFrequency);
                     }
                     
tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                     connection.rollback();
                 }
                 long seqNum = table.getSequenceNumber();
                 if (changingPhoenixTableProperty || columnDefs.size() > 0) { 
-                    seqNum = incrementTableSeqNum(table, 
statement.getTableType(), columnDefs.size(), isTransactional, isImmutableRows, 
disableWAL, multiTenant, storeNulls);
+                    seqNum = incrementTableSeqNum(table, 
statement.getTableType(), columnDefs.size(), isTransactional, 
updateCacheFrequency, isImmutableRows, disableWAL, multiTenant, storeNulls);
                     
tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                     connection.rollback();
                 }
@@ -2844,19 +2884,26 @@ public class MetaDataClient {
                     // Only update client side cache if we aren't adding a PK 
column to a table with indexes or 
                     // transitioning a table from non transactional to 
transactional.
                     // We could update the cache manually then too, it'd just 
be a pain.
+                    String fullTableName = SchemaUtil.getTableName(schemaName, 
tableName);
+                    long resolvedTimeStamp = 
TransactionUtil.getResolvedTime(connection, result);
                     if (table.getIndexes().isEmpty() || (numPkColumnsAdded==0 
&& !nonTxToTx)) {
-                        connection.addColumn(
-                                tenantId,
-                                SchemaUtil.getTableName(schemaName, tableName),
-                                columns,
-                                result.getMutationTime(),
-                                seqNum,
-                                isImmutableRows == null ? 
table.isImmutableRows() : isImmutableRows,
-                                disableWAL == null ? table.isWALDisabled() : 
disableWAL,
-                                multiTenant == null ? table.isMultiTenant() : 
multiTenant,
-                                storeNulls == null ? table.getStoreNulls() : 
storeNulls, 
-                                isTransactional == null ? 
table.isTransactional() : isTransactional,
-                                TransactionUtil.getResolvedTime(connection, 
result));
+                                               connection.addColumn(
+                                                               tenantId,
+                                                               fullTableName,
+                                                               columns,
+                                                               
result.getMutationTime(),
+                                                               seqNum,
+                                                               isImmutableRows 
== null ? table.isImmutableRows() : isImmutableRows,
+                                                               disableWAL == 
null ? table.isWALDisabled() : disableWAL,
+                                                               multiTenant == 
null ? table.isMultiTenant() : multiTenant,
+                                                               storeNulls == 
null ? table.getStoreNulls() : storeNulls, 
+                                                               isTransactional 
== null ? table.isTransactional() : isTransactional,
+                                                               
updateCacheFrequency == null ? table.getUpdateCacheFrequency() : 
updateCacheFrequency,
+                                                               
resolvedTimeStamp);
+                    } else if (updateCacheFrequency != null) {
+                        // Force removal from cache as the update cache 
frequency has changed
+                        // Note that clients outside this JVM won't be 
affected.
+                        connection.removeTable(tenantId, fullTableName, null, 
resolvedTimeStamp);
                     }
                     // Delete rows in view index if we haven't dropped it 
already
                     // We only need to do this if the multiTenant transitioned 
to false
@@ -3036,7 +3083,7 @@ public class MetaDataClient {
                         }
                     }
                     if(!indexColumnsToDrop.isEmpty()) {
-                        incrementTableSeqNum(index, index.getType(), 
-indexColumnsToDrop.size(), null);
+                        incrementTableSeqNum(index, index.getType(), 
-indexColumnsToDrop.size(), null, null);
                         dropColumnMutations(index, indexColumnsToDrop, 
tableMetaData);
                     }
 
@@ -3045,7 +3092,7 @@ public class MetaDataClient {
                 
tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                 connection.rollback();
 
-                long seqNum = incrementTableSeqNum(table, 
statement.getTableType(), -tableColumnsToDrop.size(), null);
+                long seqNum = incrementTableSeqNum(table, 
statement.getTableType(), -tableColumnsToDrop.size(), null, null);
                 
tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                 connection.rollback();
                 // Force table header to be first in list

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaData.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaData.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaData.java
index a3103bf..3adcb7e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaData.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaData.java
@@ -17,8 +17,6 @@
  */
 package org.apache.phoenix.schema;
 
-import java.sql.SQLException;
-
 import org.apache.phoenix.parse.PFunction;
 import org.apache.phoenix.query.MetaDataMutated;
 
@@ -34,4 +32,5 @@ public interface PMetaData extends MetaDataMutated, 
Iterable<PTable>, Cloneable
     public PMetaData pruneTables(Pruner pruner);
     public PFunction getFunction(PTableKey key) throws 
FunctionNotFoundException;
     public PMetaData pruneFunctions(Pruner pruner);
+    public long getAge(PTableRef ref);
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
index 9e4460d..66b4af3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
@@ -170,6 +170,10 @@ public class PMetaDataImpl implements PMetaData {
                 return put(key, new PTableRef(value, 
timeKeeper.getCurrentTime(), 0, resolvedTime));
             }
             
+            public long getAge(PTableRef ref) {
+                return timeKeeper.getCurrentTime() - ref.getCreateTime();
+            }
+            
             public PTable remove(PTableKey key) {
                 PTableRef value = this.tables.remove(key);
                 if (value == null) {
@@ -313,7 +317,7 @@ public class PMetaDataImpl implements PMetaData {
     }
 
     @Override
-    public PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columnsToAdd, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows, 
boolean isWalDisabled, boolean isMultitenant, boolean storeNulls, boolean 
isTransactional, long resolvedTime) throws SQLException {
+    public PMetaData addColumn(PName tenantId, String tableName, List<PColumn> 
columnsToAdd, long tableTimeStamp, long tableSeqNum, boolean isImmutableRows, 
boolean isWalDisabled, boolean isMultitenant, boolean storeNulls, boolean 
isTransactional, long updateCacheFrequency, long resolvedTime) throws 
SQLException {
         PTableRef oldTableRef = metaData.get(new PTableKey(tenantId, 
tableName));
         if (oldTableRef == null) {
             return this;
@@ -327,7 +331,9 @@ public class PMetaDataImpl implements PMetaData {
             newColumns.addAll(oldColumns);
             newColumns.addAll(columnsToAdd);
         }
-        PTable newTable = PTableImpl.makePTable(oldTableRef.getTable(), 
tableTimeStamp, tableSeqNum, newColumns, isImmutableRows, isWalDisabled, 
isMultitenant, storeNulls, isTransactional);
+        PTable newTable = PTableImpl.makePTable(oldTableRef.getTable(),
+                tableTimeStamp, tableSeqNum, newColumns, isImmutableRows,
+                isWalDisabled, isMultitenant, storeNulls, isTransactional, 
updateCacheFrequency);
         return addTable(newTable, resolvedTime);
     }
 
@@ -472,4 +478,9 @@ public class PMetaDataImpl implements PMetaData {
         return new PMetaDataImpl(clone);
     
     }
+
+    @Override
+    public long getAge(PTableRef ref) {
+        return this.metaData.getAge(ref);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/59b336ec/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
index ec97394..4a338f6 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
@@ -339,4 +339,5 @@ public interface PTable extends PMetaDataEntity {
      * -1 if there is no such column.
      */
     int getRowTimestampColPos();
+    long getUpdateCacheFrequency();
 }

Reply via email to