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.coprocessor; 019 020import java.io.IOException; 021import java.util.Arrays; 022import java.util.List; 023import java.util.Optional; 024import org.apache.hadoop.fs.Path; 025import org.apache.hadoop.hbase.Cell; 026import org.apache.hadoop.hbase.CellUtil; 027import org.apache.hadoop.hbase.KeyValue; 028import org.apache.hadoop.hbase.client.RegionInfo; 029import org.apache.hadoop.hbase.util.Bytes; 030import org.apache.hadoop.hbase.wal.WALEdit; 031import org.apache.hadoop.hbase.wal.WALKey; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035/** 036 * Class for testing WALObserver coprocessor. It will monitor WAL writing and restoring, and modify 037 * passed-in WALEdit, i.e, ignore specified columns when writing, or add a KeyValue. On the other 038 * side, it checks whether the ignored column is still in WAL when Restoreed at region reconstruct. 039 */ 040public class SampleRegionWALCoprocessor 041 implements WALCoprocessor, RegionCoprocessor, WALObserver, RegionObserver { 042 043 private static final Logger LOG = LoggerFactory.getLogger(SampleRegionWALCoprocessor.class); 044 045 private byte[] tableName; 046 private byte[] row; 047 private byte[] ignoredFamily; 048 private byte[] ignoredQualifier; 049 private byte[] addedFamily; 050 private byte[] addedQualifier; 051 private byte[] changedFamily; 052 private byte[] changedQualifier; 053 054 private boolean preWALWriteCalled = false; 055 private boolean postWALWriteCalled = false; 056 private boolean preWALRollCalled = false; 057 private boolean postWALRollCalled = false; 058 private boolean preReplayWALsCalled = false; 059 private boolean postReplayWALsCalled = false; 060 061 /** 062 * Set values: with a table name, a column name which will be ignored, and a column name which 063 * will be added to WAL. 064 */ 065 public void setTestValues(byte[] tableName, byte[] row, byte[] igf, byte[] igq, byte[] chf, 066 byte[] chq, byte[] addf, byte[] addq) { 067 this.row = row; 068 this.tableName = tableName; 069 this.ignoredFamily = igf; 070 this.ignoredQualifier = igq; 071 this.addedFamily = addf; 072 this.addedQualifier = addq; 073 this.changedFamily = chf; 074 this.changedQualifier = chq; 075 preWALWriteCalled = false; 076 postWALWriteCalled = false; 077 preWALRollCalled = false; 078 postWALRollCalled = false; 079 } 080 081 @Override 082 public Optional<WALObserver> getWALObserver() { 083 return Optional.of(this); 084 } 085 086 @Override 087 public Optional<RegionObserver> getRegionObserver() { 088 return Optional.of(this); 089 } 090 091 @Override 092 public void postWALWrite(ObserverContext<? extends WALCoprocessorEnvironment> env, 093 RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException { 094 postWALWriteCalled = true; 095 } 096 097 @Override 098 public void preWALWrite(ObserverContext<? extends WALCoprocessorEnvironment> env, RegionInfo info, 099 WALKey logKey, WALEdit logEdit) throws IOException { 100 // check table name matches or not. 101 if (!Bytes.equals(info.getTable().toBytes(), this.tableName)) { 102 return; 103 } 104 preWALWriteCalled = true; 105 // here we're going to remove one keyvalue from the WALEdit, and add 106 // another one to it. 107 List<Cell> cells = logEdit.getCells(); 108 Cell deletedCell = null; 109 for (Cell cell : cells) { 110 // assume only one kv from the WALEdit matches. 111 byte[] family = CellUtil.cloneFamily(cell); 112 byte[] qulifier = CellUtil.cloneQualifier(cell); 113 114 if (Arrays.equals(family, ignoredFamily) && Arrays.equals(qulifier, ignoredQualifier)) { 115 LOG.debug("Found the KeyValue from WALEdit which should be ignored."); 116 deletedCell = cell; 117 } 118 if (Arrays.equals(family, changedFamily) && Arrays.equals(qulifier, changedQualifier)) { 119 LOG.debug("Found the KeyValue from WALEdit which should be changed."); 120 cell.getValueArray()[cell.getValueOffset()] = 121 (byte) (cell.getValueArray()[cell.getValueOffset()] + 1); 122 } 123 } 124 if (null != row) { 125 cells.add(new KeyValue(row, addedFamily, addedQualifier)); 126 } 127 if (deletedCell != null) { 128 LOG.debug("About to delete a KeyValue from WALEdit."); 129 cells.remove(deletedCell); 130 } 131 } 132 133 @Override 134 public void preWALRoll(ObserverContext<? extends WALCoprocessorEnvironment> ctx, Path oldPath, 135 Path newPath) throws IOException { 136 preWALRollCalled = true; 137 } 138 139 @Override 140 public void postWALRoll(ObserverContext<? extends WALCoprocessorEnvironment> ctx, Path oldPath, 141 Path newPath) throws IOException { 142 postWALRollCalled = true; 143 } 144 145 @Override 146 public void preReplayWALs(ObserverContext<? extends RegionCoprocessorEnvironment> ctx, 147 RegionInfo info, Path edits) throws IOException { 148 preReplayWALsCalled = true; 149 } 150 151 @Override 152 public void postReplayWALs(ObserverContext<? extends RegionCoprocessorEnvironment> ctx, 153 RegionInfo info, Path edits) throws IOException { 154 postReplayWALsCalled = true; 155 } 156 157 public boolean isPreWALWriteCalled() { 158 return preWALWriteCalled; 159 } 160 161 public boolean isPostWALWriteCalled() { 162 return postWALWriteCalled; 163 } 164 165 public boolean isPreWALRollCalled() { 166 return preWALRollCalled; 167 } 168 169 public boolean isPostWALRollCalled() { 170 return postWALRollCalled; 171 } 172 173 public boolean isPreReplayWALsCalled() { 174 return preReplayWALsCalled; 175 } 176 177 public boolean isPostReplayWALsCalled() { 178 return postReplayWALsCalled; 179 } 180}