001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.replication; 019 020import static org.junit.Assert.assertNull; 021import static org.junit.Assert.assertTrue; 022import static org.mockito.Mockito.mock; 023import static org.mockito.Mockito.when; 024 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031import java.util.TreeMap; 032import org.apache.hadoop.hbase.Cell; 033import org.apache.hadoop.hbase.CellComparatorImpl; 034import org.apache.hadoop.hbase.HBaseClassTestRule; 035import org.apache.hadoop.hbase.HConstants; 036import org.apache.hadoop.hbase.KeyValue; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.RegionInfoBuilder; 039import org.apache.hadoop.hbase.testclassification.ReplicationTests; 040import org.apache.hadoop.hbase.testclassification.SmallTests; 041import org.apache.hadoop.hbase.util.Bytes; 042import org.apache.hadoop.hbase.wal.WAL.Entry; 043import org.apache.hadoop.hbase.wal.WALEdit; 044import org.apache.hadoop.hbase.wal.WALKeyImpl; 045import org.junit.Assert; 046import org.junit.ClassRule; 047import org.junit.Test; 048import org.junit.experimental.categories.Category; 049 050import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 051 052@Category({ ReplicationTests.class, SmallTests.class }) 053public class TestReplicationWALEntryFilters { 054 055 @ClassRule 056 public static final HBaseClassTestRule CLASS_RULE = 057 HBaseClassTestRule.forClass(TestReplicationWALEntryFilters.class); 058 059 static byte[] a = new byte[] {'a'}; 060 static byte[] b = new byte[] {'b'}; 061 static byte[] c = new byte[] {'c'}; 062 static byte[] d = new byte[] {'d'}; 063 064 @Test 065 public void testSystemTableWALEntryFilter() { 066 SystemTableWALEntryFilter filter = new SystemTableWALEntryFilter(); 067 068 // meta 069 WALKeyImpl key1 = 070 new WALKeyImpl(RegionInfoBuilder.FIRST_META_REGIONINFO.getEncodedNameAsBytes(), 071 TableName.META_TABLE_NAME, System.currentTimeMillis()); 072 Entry metaEntry = new Entry(key1, null); 073 074 assertNull(filter.filter(metaEntry)); 075 076 // ns table 077 WALKeyImpl key2 = 078 new WALKeyImpl(new byte[0], TableName.NAMESPACE_TABLE_NAME, System.currentTimeMillis()); 079 Entry nsEntry = new Entry(key2, null); 080 assertNull(filter.filter(nsEntry)); 081 082 // user table 083 084 WALKeyImpl key3 = new WALKeyImpl(new byte[0], TableName.valueOf("foo"), 085 System.currentTimeMillis()); 086 Entry userEntry = new Entry(key3, null); 087 088 assertEquals(userEntry, filter.filter(userEntry)); 089 } 090 091 @Test 092 public void testScopeWALEntryFilter() { 093 WALEntryFilter filter = new ChainWALEntryFilter(new ScopeWALEntryFilter()); 094 095 Entry userEntry = createEntry(null, a, b); 096 Entry userEntryA = createEntry(null, a); 097 Entry userEntryB = createEntry(null, b); 098 Entry userEntryEmpty = createEntry(null); 099 100 // no scopes 101 // now we will not filter out entries without a replication scope since serial replication still 102 // need the sequence id, but the cells will all be filtered out. 103 assertTrue(filter.filter(userEntry).getEdit().isEmpty()); 104 105 // empty scopes 106 // ditto 107 TreeMap<byte[], Integer> scopes = new TreeMap<>(Bytes.BYTES_COMPARATOR); 108 userEntry = createEntry(scopes, a, b); 109 assertTrue(filter.filter(userEntry).getEdit().isEmpty()); 110 111 // different scope 112 scopes = new TreeMap<>(Bytes.BYTES_COMPARATOR); 113 scopes.put(c, HConstants.REPLICATION_SCOPE_GLOBAL); 114 userEntry = createEntry(scopes, a, b); 115 // all kvs should be filtered 116 assertEquals(userEntryEmpty, filter.filter(userEntry)); 117 118 // local scope 119 scopes = new TreeMap<>(Bytes.BYTES_COMPARATOR); 120 scopes.put(a, HConstants.REPLICATION_SCOPE_LOCAL); 121 userEntry = createEntry(scopes, a, b); 122 assertEquals(userEntryEmpty, filter.filter(userEntry)); 123 scopes.put(b, HConstants.REPLICATION_SCOPE_LOCAL); 124 assertEquals(userEntryEmpty, filter.filter(userEntry)); 125 126 // only scope a 127 scopes = new TreeMap<>(Bytes.BYTES_COMPARATOR); 128 scopes.put(a, HConstants.REPLICATION_SCOPE_GLOBAL); 129 userEntry = createEntry(scopes, a, b); 130 assertEquals(userEntryA, filter.filter(userEntry)); 131 scopes.put(b, HConstants.REPLICATION_SCOPE_LOCAL); 132 assertEquals(userEntryA, filter.filter(userEntry)); 133 134 // only scope b 135 scopes = new TreeMap<>(Bytes.BYTES_COMPARATOR); 136 scopes.put(b, HConstants.REPLICATION_SCOPE_GLOBAL); 137 userEntry = createEntry(scopes, a, b); 138 assertEquals(userEntryB, filter.filter(userEntry)); 139 scopes.put(a, HConstants.REPLICATION_SCOPE_LOCAL); 140 assertEquals(userEntryB, filter.filter(userEntry)); 141 142 // scope a and b 143 scopes = new TreeMap<>(Bytes.BYTES_COMPARATOR); 144 scopes.put(b, HConstants.REPLICATION_SCOPE_GLOBAL); 145 userEntry = createEntry(scopes, a, b); 146 assertEquals(userEntryB, filter.filter(userEntry)); 147 scopes.put(a, HConstants.REPLICATION_SCOPE_LOCAL); 148 assertEquals(userEntryB, filter.filter(userEntry)); 149 } 150 151 WALEntryFilter nullFilter = new WALEntryFilter() { 152 @Override 153 public Entry filter(Entry entry) { 154 return null; 155 } 156 }; 157 158 WALEntryFilter passFilter = new WALEntryFilter() { 159 @Override 160 public Entry filter(Entry entry) { 161 return entry; 162 } 163 }; 164 165 @Test 166 public void testChainWALEntryFilter() { 167 Entry userEntry = createEntry(null, a, b, c); 168 169 ChainWALEntryFilter filter = new ChainWALEntryFilter(passFilter); 170 assertEquals(createEntry(null, a,b,c), filter.filter(userEntry)); 171 172 filter = new ChainWALEntryFilter(passFilter, passFilter); 173 assertEquals(createEntry(null, a,b,c), filter.filter(userEntry)); 174 175 filter = new ChainWALEntryFilter(passFilter, passFilter, passFilter); 176 assertEquals(createEntry(null, a,b,c), filter.filter(userEntry)); 177 178 filter = new ChainWALEntryFilter(nullFilter); 179 assertEquals(null, filter.filter(userEntry)); 180 181 filter = new ChainWALEntryFilter(nullFilter, passFilter); 182 assertEquals(null, filter.filter(userEntry)); 183 184 filter = new ChainWALEntryFilter(passFilter, nullFilter); 185 assertEquals(null, filter.filter(userEntry)); 186 187 filter = new ChainWALEntryFilter(nullFilter, passFilter, nullFilter); 188 assertEquals(null, filter.filter(userEntry)); 189 190 filter = new ChainWALEntryFilter(nullFilter, nullFilter); 191 assertEquals(null, filter.filter(userEntry)); 192 193 // flatten 194 filter = 195 new ChainWALEntryFilter( 196 new ChainWALEntryFilter(passFilter, 197 new ChainWALEntryFilter(passFilter, passFilter), 198 new ChainWALEntryFilter(passFilter), 199 new ChainWALEntryFilter(passFilter)), 200 new ChainWALEntryFilter(passFilter)); 201 assertEquals(createEntry(null, a,b,c), filter.filter(userEntry)); 202 203 204 filter = 205 new ChainWALEntryFilter( 206 new ChainWALEntryFilter(passFilter, 207 new ChainWALEntryFilter(passFilter, 208 new ChainWALEntryFilter(nullFilter))), 209 new ChainWALEntryFilter(passFilter)); 210 assertEquals(null, filter.filter(userEntry)); 211 } 212 213 @Test 214 public void testNamespaceTableCfWALEntryFilter() { 215 ReplicationPeer peer = mock(ReplicationPeer.class); 216 ReplicationPeerConfig peerConfig = mock(ReplicationPeerConfig.class); 217 218 // 1. replicate_all flag is false, no namespaces and table-cfs config 219 when(peerConfig.replicateAllUserTables()).thenReturn(false); 220 when(peerConfig.getNamespaces()).thenReturn(null); 221 when(peerConfig.getTableCFsMap()).thenReturn(null); 222 when(peer.getPeerConfig()).thenReturn(peerConfig); 223 Entry userEntry = createEntry(null, a, b, c); 224 ChainWALEntryFilter filter = 225 new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 226 assertEquals(null, filter.filter(userEntry)); 227 228 // 2. replicate_all flag is false, and only config table-cfs in peer 229 // empty map 230 userEntry = createEntry(null, a, b, c); 231 Map<TableName, List<String>> tableCfs = new HashMap<>(); 232 when(peerConfig.replicateAllUserTables()).thenReturn(false); 233 when(peerConfig.getTableCFsMap()).thenReturn(tableCfs); 234 when(peer.getPeerConfig()).thenReturn(peerConfig); 235 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 236 assertEquals(null, filter.filter(userEntry)); 237 238 // table bar 239 userEntry = createEntry(null, a, b, c); 240 tableCfs = new HashMap<>(); 241 tableCfs.put(TableName.valueOf("bar"), null); 242 when(peerConfig.replicateAllUserTables()).thenReturn(false); 243 when(peerConfig.getTableCFsMap()).thenReturn(tableCfs); 244 when(peer.getPeerConfig()).thenReturn(peerConfig); 245 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 246 assertEquals(null, filter.filter(userEntry)); 247 248 // table foo:a 249 userEntry = createEntry(null, a, b, c); 250 tableCfs = new HashMap<>(); 251 tableCfs.put(TableName.valueOf("foo"), Lists.newArrayList("a")); 252 when(peerConfig.replicateAllUserTables()).thenReturn(false); 253 when(peerConfig.getTableCFsMap()).thenReturn(tableCfs); 254 when(peer.getPeerConfig()).thenReturn(peerConfig); 255 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 256 assertEquals(createEntry(null, a), filter.filter(userEntry)); 257 258 // table foo:a,c 259 userEntry = createEntry(null, a, b, c, d); 260 tableCfs = new HashMap<>(); 261 tableCfs.put(TableName.valueOf("foo"), Lists.newArrayList("a", "c")); 262 when(peerConfig.replicateAllUserTables()).thenReturn(false); 263 when(peerConfig.getTableCFsMap()).thenReturn(tableCfs); 264 when(peer.getPeerConfig()).thenReturn(peerConfig); 265 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 266 assertEquals(createEntry(null, a,c), filter.filter(userEntry)); 267 268 // 3. replicate_all flag is false, and only config namespaces in peer 269 when(peer.getTableCFs()).thenReturn(null); 270 // empty set 271 Set<String> namespaces = new HashSet<>(); 272 when(peerConfig.replicateAllUserTables()).thenReturn(false); 273 when(peerConfig.getNamespaces()).thenReturn(namespaces); 274 when(peerConfig.getTableCFsMap()).thenReturn(null); 275 when(peer.getPeerConfig()).thenReturn(peerConfig); 276 userEntry = createEntry(null, a, b, c); 277 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 278 assertEquals(null, filter.filter(userEntry)); 279 280 // namespace default 281 namespaces.add("default"); 282 when(peerConfig.replicateAllUserTables()).thenReturn(false); 283 when(peerConfig.getNamespaces()).thenReturn(namespaces); 284 when(peer.getPeerConfig()).thenReturn(peerConfig); 285 userEntry = createEntry(null, a, b, c); 286 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 287 assertEquals(createEntry(null, a,b,c), filter.filter(userEntry)); 288 289 // namespace ns1 290 namespaces = new HashSet<>(); 291 namespaces.add("ns1"); 292 when(peerConfig.replicateAllUserTables()).thenReturn(false); 293 when(peerConfig.getNamespaces()).thenReturn(namespaces); 294 when(peer.getPeerConfig()).thenReturn(peerConfig); 295 userEntry = createEntry(null, a, b, c); 296 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 297 assertEquals(null, filter.filter(userEntry)); 298 299 // 4. replicate_all flag is false, and config namespaces and table-cfs both 300 // Namespaces config should not confict with table-cfs config 301 namespaces = new HashSet<>(); 302 tableCfs = new HashMap<>(); 303 namespaces.add("ns1"); 304 tableCfs.put(TableName.valueOf("foo"), Lists.newArrayList("a", "c")); 305 when(peerConfig.replicateAllUserTables()).thenReturn(false); 306 when(peerConfig.getNamespaces()).thenReturn(namespaces); 307 when(peerConfig.getTableCFsMap()).thenReturn(tableCfs); 308 when(peer.getPeerConfig()).thenReturn(peerConfig); 309 userEntry = createEntry(null, a, b, c); 310 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 311 assertEquals(createEntry(null, a, c), filter.filter(userEntry)); 312 313 namespaces = new HashSet<>(); 314 tableCfs = new HashMap<>(); 315 namespaces.add("default"); 316 tableCfs.put(TableName.valueOf("ns1:foo"), Lists.newArrayList("a", "c")); 317 when(peerConfig.replicateAllUserTables()).thenReturn(false); 318 when(peerConfig.getNamespaces()).thenReturn(namespaces); 319 when(peerConfig.getTableCFsMap()).thenReturn(tableCfs); 320 when(peer.getPeerConfig()).thenReturn(peerConfig); 321 userEntry = createEntry(null, a, b, c); 322 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 323 assertEquals(createEntry(null, a, b, c), filter.filter(userEntry)); 324 325 namespaces = new HashSet<>(); 326 tableCfs = new HashMap<>(); 327 namespaces.add("ns1"); 328 tableCfs.put(TableName.valueOf("bar"), null); 329 when(peerConfig.replicateAllUserTables()).thenReturn(false); 330 when(peerConfig.getNamespaces()).thenReturn(namespaces); 331 when(peerConfig.getTableCFsMap()).thenReturn(tableCfs); 332 when(peer.getPeerConfig()).thenReturn(peerConfig); 333 userEntry = createEntry(null, a, b, c); 334 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 335 assertEquals(null, filter.filter(userEntry)); 336 } 337 338 @Test 339 public void testNamespaceTableCfWALEntryFilter2() { 340 ReplicationPeer peer = mock(ReplicationPeer.class); 341 ReplicationPeerConfig peerConfig = mock(ReplicationPeerConfig.class); 342 343 // 1. replicate_all flag is true 344 // and no exclude namespaces and no exclude table-cfs config 345 when(peerConfig.replicateAllUserTables()).thenReturn(true); 346 when(peerConfig.getExcludeNamespaces()).thenReturn(null); 347 when(peerConfig.getExcludeTableCFsMap()).thenReturn(null); 348 when(peer.getPeerConfig()).thenReturn(peerConfig); 349 Entry userEntry = createEntry(null, a, b, c); 350 ChainWALEntryFilter filter = 351 new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 352 assertEquals(createEntry(null, a, b, c), filter.filter(userEntry)); 353 354 // 2. replicate_all flag is true, and only config exclude namespaces 355 // empty set 356 Set<String> namespaces = new HashSet<String>(); 357 when(peerConfig.getExcludeNamespaces()).thenReturn(namespaces); 358 when(peerConfig.getExcludeTableCFsMap()).thenReturn(null); 359 when(peer.getPeerConfig()).thenReturn(peerConfig); 360 userEntry = createEntry(null, a, b, c); 361 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 362 assertEquals(createEntry(null, a, b, c), filter.filter(userEntry)); 363 364 // exclude namespace default 365 namespaces.add("default"); 366 when(peerConfig.getExcludeNamespaces()).thenReturn(namespaces); 367 when(peerConfig.getExcludeTableCFsMap()).thenReturn(null); 368 when(peer.getPeerConfig()).thenReturn(peerConfig); 369 userEntry = createEntry(null, a, b, c); 370 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 371 assertEquals(null, filter.filter(userEntry)); 372 373 // exclude namespace ns1 374 namespaces = new HashSet<String>(); 375 namespaces.add("ns1"); 376 when(peerConfig.getExcludeNamespaces()).thenReturn(namespaces); 377 when(peerConfig.getExcludeTableCFsMap()).thenReturn(null); 378 when(peer.getPeerConfig()).thenReturn(peerConfig); 379 userEntry = createEntry(null, a, b, c); 380 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 381 assertEquals(createEntry(null, a, b, c), filter.filter(userEntry)); 382 383 // 3. replicate_all flag is true, and only config exclude table-cfs 384 // empty table-cfs map 385 Map<TableName, List<String>> tableCfs = new HashMap<TableName, List<String>>(); 386 when(peerConfig.getExcludeNamespaces()).thenReturn(null); 387 when(peerConfig.getExcludeTableCFsMap()).thenReturn(tableCfs); 388 when(peer.getPeerConfig()).thenReturn(peerConfig); 389 userEntry = createEntry(null, a, b, c); 390 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 391 assertEquals(createEntry(null, a, b, c), filter.filter(userEntry)); 392 393 // exclude table bar 394 tableCfs = new HashMap<TableName, List<String>>(); 395 tableCfs.put(TableName.valueOf("bar"), null); 396 when(peerConfig.getExcludeNamespaces()).thenReturn(null); 397 when(peerConfig.getExcludeTableCFsMap()).thenReturn(tableCfs); 398 when(peer.getPeerConfig()).thenReturn(peerConfig); 399 userEntry = createEntry(null, a, b, c); 400 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 401 assertEquals(createEntry(null, a, b, c), filter.filter(userEntry)); 402 403 // exclude table foo:a 404 tableCfs = new HashMap<TableName, List<String>>(); 405 tableCfs.put(TableName.valueOf("foo"), Lists.newArrayList("a")); 406 when(peerConfig.getExcludeNamespaces()).thenReturn(null); 407 when(peerConfig.getExcludeTableCFsMap()).thenReturn(tableCfs); 408 when(peer.getPeerConfig()).thenReturn(peerConfig); 409 userEntry = createEntry(null, a, b, c); 410 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 411 assertEquals(createEntry(null, b, c), filter.filter(userEntry)); 412 413 // 4. replicate_all flag is true, and config exclude namespaces and table-cfs both 414 // exclude ns1 and table foo:a,c 415 namespaces = new HashSet<String>(); 416 tableCfs = new HashMap<TableName, List<String>>(); 417 namespaces.add("ns1"); 418 tableCfs.put(TableName.valueOf("foo"), Lists.newArrayList("a", "c")); 419 when(peerConfig.getExcludeNamespaces()).thenReturn(namespaces); 420 when(peerConfig.getExcludeTableCFsMap()).thenReturn(tableCfs); 421 when(peer.getPeerConfig()).thenReturn(peerConfig); 422 userEntry = createEntry(null, a, b, c); 423 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 424 assertEquals(createEntry(null, b), filter.filter(userEntry)); 425 426 // exclude namespace default and table ns1:bar 427 namespaces = new HashSet<String>(); 428 tableCfs = new HashMap<TableName, List<String>>(); 429 namespaces.add("default"); 430 tableCfs.put(TableName.valueOf("ns1:bar"), new ArrayList<String>()); 431 when(peerConfig.getExcludeNamespaces()).thenReturn(namespaces); 432 when(peerConfig.getExcludeTableCFsMap()).thenReturn(tableCfs); 433 when(peer.getPeerConfig()).thenReturn(peerConfig); 434 userEntry = createEntry(null, a, b, c); 435 filter = new ChainWALEntryFilter(new NamespaceTableCfWALEntryFilter(peer)); 436 assertEquals(null, filter.filter(userEntry)); 437 } 438 439 private Entry createEntry(TreeMap<byte[], Integer> scopes, byte[]... kvs) { 440 WALKeyImpl key1 = new WALKeyImpl(new byte[0], TableName.valueOf("foo"), 441 System.currentTimeMillis(), scopes); 442 WALEdit edit1 = new WALEdit(); 443 444 for (byte[] kv : kvs) { 445 edit1.add(new KeyValue(kv, kv, kv)); 446 } 447 return new Entry(key1, edit1); 448 } 449 450 private void assertEquals(Entry e1, Entry e2) { 451 Assert.assertEquals(e1 == null, e2 == null); 452 if (e1 == null) { 453 return; 454 } 455 456 // do not compare WALKeys 457 458 // compare kvs 459 Assert.assertEquals(e1.getEdit() == null, e2.getEdit() == null); 460 if (e1.getEdit() == null) { 461 return; 462 } 463 List<Cell> cells1 = e1.getEdit().getCells(); 464 List<Cell> cells2 = e2.getEdit().getCells(); 465 Assert.assertEquals(cells1.size(), cells2.size()); 466 for (int i = 0; i < cells1.size(); i++) { 467 CellComparatorImpl.COMPARATOR.compare(cells1.get(i), cells2.get(i)); 468 } 469 } 470}