001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020package org.apache.hadoop.hbase.coprocessor; 021 022import java.io.IOException; 023import java.util.Arrays; 024import java.util.List; 025import java.util.Optional; 026 027import org.apache.hadoop.fs.Path; 028import org.apache.hadoop.hbase.Cell; 029import org.apache.hadoop.hbase.CellUtil; 030import org.apache.hadoop.hbase.KeyValue; 031import org.apache.hadoop.hbase.client.RegionInfo; 032import org.apache.hadoop.hbase.util.Bytes; 033import org.apache.hadoop.hbase.wal.WALEdit; 034import org.apache.hadoop.hbase.wal.WALKey; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038/** 039 * Class for testing WALObserver coprocessor. It will monitor WAL writing and restoring, and modify 040 * passed-in WALEdit, i.e, ignore specified columns when writing, or add a KeyValue. On the other 041 * side, it checks whether the ignored column is still in WAL when Restoreed at region reconstruct. 042 */ 043public class SampleRegionWALCoprocessor implements WALCoprocessor, RegionCoprocessor, 044 WALObserver, RegionObserver { 045 046 private static final Logger LOG = LoggerFactory.getLogger(SampleRegionWALCoprocessor.class); 047 048 private byte[] tableName; 049 private byte[] row; 050 private byte[] ignoredFamily; 051 private byte[] ignoredQualifier; 052 private byte[] addedFamily; 053 private byte[] addedQualifier; 054 private byte[] changedFamily; 055 private byte[] changedQualifier; 056 057 private boolean preWALWriteCalled = false; 058 private boolean postWALWriteCalled = false; 059 private boolean preWALRestoreCalled = false; 060 private boolean postWALRestoreCalled = false; 061 private boolean preWALRollCalled = false; 062 private boolean postWALRollCalled = false; 063 064 /** 065 * Set values: with a table name, a column name which will be ignored, and 066 * a column name which will be added to WAL. 067 */ 068 public void setTestValues(byte[] tableName, byte[] row, byte[] igf, byte[] igq, 069 byte[] chf, byte[] chq, byte[] addf, byte[] addq) { 070 this.row = row; 071 this.tableName = tableName; 072 this.ignoredFamily = igf; 073 this.ignoredQualifier = igq; 074 this.addedFamily = addf; 075 this.addedQualifier = addq; 076 this.changedFamily = chf; 077 this.changedQualifier = chq; 078 preWALWriteCalled = false; 079 postWALWriteCalled = false; 080 preWALRestoreCalled = false; 081 postWALRestoreCalled = false; 082 preWALRollCalled = false; 083 postWALRollCalled = false; 084 } 085 086 @Override public Optional<WALObserver> getWALObserver() { 087 return Optional.of(this); 088 } 089 090 @Override 091 public Optional<RegionObserver> getRegionObserver() { 092 return Optional.of(this); 093 } 094 095 @Override 096 public void postWALWrite(ObserverContext<? extends WALCoprocessorEnvironment> env, 097 RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException { 098 postWALWriteCalled = true; 099 } 100 101 @Override 102 public void preWALWrite(ObserverContext<? extends WALCoprocessorEnvironment> env, 103 RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException { 104 // check table name matches or not. 105 if (!Bytes.equals(info.getTable().toBytes(), this.tableName)) { 106 return; 107 } 108 preWALWriteCalled = true; 109 // here we're going to remove one keyvalue from the WALEdit, and add 110 // another one to it. 111 List<Cell> cells = logEdit.getCells(); 112 Cell deletedCell = null; 113 for (Cell cell : cells) { 114 // assume only one kv from the WALEdit matches. 115 byte[] family = CellUtil.cloneFamily(cell); 116 byte[] qulifier = CellUtil.cloneQualifier(cell); 117 118 if (Arrays.equals(family, ignoredFamily) && 119 Arrays.equals(qulifier, ignoredQualifier)) { 120 LOG.debug("Found the KeyValue from WALEdit which should be ignored."); 121 deletedCell = cell; 122 } 123 if (Arrays.equals(family, changedFamily) && 124 Arrays.equals(qulifier, changedQualifier)) { 125 LOG.debug("Found the KeyValue from WALEdit which should be changed."); 126 cell.getValueArray()[cell.getValueOffset()] = 127 (byte) (cell.getValueArray()[cell.getValueOffset()] + 1); 128 } 129 } 130 if (null != row) { 131 cells.add(new KeyValue(row, addedFamily, addedQualifier)); 132 } 133 if (deletedCell != null) { 134 LOG.debug("About to delete a KeyValue from WALEdit."); 135 cells.remove(deletedCell); 136 } 137 } 138 139 /** 140 * Triggered before {@link org.apache.hadoop.hbase.regionserver.HRegion} when WAL is 141 * Restoreed. 142 */ 143 @Override 144 public void preWALRestore(ObserverContext<? extends RegionCoprocessorEnvironment> env, 145 RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException { 146 preWALRestoreCalled = true; 147 } 148 149 @Override 150 public void preWALRoll(ObserverContext<? extends WALCoprocessorEnvironment> ctx, 151 Path oldPath, Path newPath) throws IOException { 152 preWALRollCalled = true; 153 } 154 155 @Override 156 public void postWALRoll(ObserverContext<? extends WALCoprocessorEnvironment> ctx, 157 Path oldPath, Path newPath) throws IOException { 158 postWALRollCalled = true; 159 } 160 161 /** 162 * Triggered after {@link org.apache.hadoop.hbase.regionserver.HRegion} when WAL is 163 * Restoreed. 164 */ 165 @Override 166 public void postWALRestore(ObserverContext<? extends RegionCoprocessorEnvironment> env, 167 RegionInfo info, WALKey logKey, WALEdit logEdit) throws IOException { 168 postWALRestoreCalled = true; 169 } 170 171 public boolean isPreWALWriteCalled() { 172 return preWALWriteCalled; 173 } 174 175 public boolean isPostWALWriteCalled() { 176 return postWALWriteCalled; 177 } 178 179 public boolean isPreWALRestoreCalled() { 180 LOG.debug(SampleRegionWALCoprocessor.class.getName() + 181 ".isPreWALRestoreCalled is called."); 182 return preWALRestoreCalled; 183 } 184 185 public boolean isPostWALRestoreCalled() { 186 LOG.debug(SampleRegionWALCoprocessor.class.getName() + 187 ".isPostWALRestoreCalled is called."); 188 return postWALRestoreCalled; 189 } 190 191 public boolean isPreWALRollCalled() { 192 return preWALRollCalled; 193 } 194 195 public boolean isPostWALRollCalled() { 196 return postWALRollCalled; 197 } 198}