View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.regionserver.wal;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertNotNull;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.io.IOException;
27  import java.lang.reflect.Field;
28  import java.util.ArrayList;
29  import java.util.Comparator;
30  import java.util.List;
31  import java.util.Set;
32  import java.util.UUID;
33  import java.util.concurrent.CountDownLatch;
34  import java.util.concurrent.ExecutorService;
35  import java.util.concurrent.Executors;
36  import java.util.concurrent.atomic.AtomicBoolean;
37  
38  import org.apache.commons.lang.mutable.MutableBoolean;
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.conf.Configuration;
42  import org.apache.hadoop.fs.FileStatus;
43  import org.apache.hadoop.fs.FileSystem;
44  import org.apache.hadoop.fs.Path;
45  import org.apache.hadoop.hbase.CellScanner;
46  import org.apache.hadoop.hbase.Coprocessor;
47  import org.apache.hadoop.hbase.HBaseConfiguration;
48  import org.apache.hadoop.hbase.HBaseTestingUtility;
49  import org.apache.hadoop.hbase.HColumnDescriptor;
50  import org.apache.hadoop.hbase.HConstants;
51  import org.apache.hadoop.hbase.HRegionInfo;
52  import org.apache.hadoop.hbase.HTableDescriptor;
53  import org.apache.hadoop.hbase.KeyValue;
54  import org.apache.hadoop.hbase.TableName;
55  import org.apache.hadoop.hbase.client.Get;
56  import org.apache.hadoop.hbase.client.Put;
57  import org.apache.hadoop.hbase.client.Result;
58  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
59  import org.apache.hadoop.hbase.coprocessor.SampleRegionWALObserver;
60  import org.apache.hadoop.hbase.regionserver.HRegion;
61  import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl;
62  import org.apache.hadoop.hbase.regionserver.Region;
63  import org.apache.hadoop.hbase.testclassification.MediumTests;
64  import org.apache.hadoop.hbase.util.Bytes;
65  import org.apache.hadoop.hbase.util.EnvironmentEdge;
66  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
67  import org.apache.hadoop.hbase.util.FSUtils;
68  import org.apache.hadoop.hbase.util.Threads;
69  import org.apache.hadoop.hbase.wal.DefaultWALProvider;
70  import org.apache.hadoop.hbase.wal.WAL;
71  import org.apache.hadoop.hbase.wal.WALKey;
72  import org.junit.After;
73  import org.junit.AfterClass;
74  import org.junit.Before;
75  import org.junit.BeforeClass;
76  import org.junit.Rule;
77  import org.junit.Test;
78  import org.junit.experimental.categories.Category;
79  import org.junit.rules.TestName;
80  
81  /**
82   * Provides FSHLog test cases.
83   */
84  @Category(MediumTests.class)
85  public class TestFSHLog {
86    private static final Log LOG = LogFactory.getLog(TestFSHLog.class);
87  
88    protected static Configuration conf;
89    protected static FileSystem fs;
90    protected static Path dir;
91    protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
92  
93    @Rule
94    public final TestName currentTest = new TestName();
95  
96    @Before
97    public void setUp() throws Exception {
98      FileStatus[] entries = fs.listStatus(new Path("/"));
99      for (FileStatus dir : entries) {
100       fs.delete(dir.getPath(), true);
101     }
102     final Path hbaseDir = TEST_UTIL.createRootDir();
103     dir = new Path(hbaseDir, currentTest.getMethodName());
104   }
105 
106   @After
107   public void tearDown() throws Exception {
108   }
109 
110   @BeforeClass
111   public static void setUpBeforeClass() throws Exception {
112     // Make block sizes small.
113     TEST_UTIL.getConfiguration().setInt("dfs.blocksize", 1024 * 1024);
114     // quicker heartbeat interval for faster DN death notification
115     TEST_UTIL.getConfiguration().setInt("dfs.namenode.heartbeat.recheck-interval", 5000);
116     TEST_UTIL.getConfiguration().setInt("dfs.heartbeat.interval", 1);
117     TEST_UTIL.getConfiguration().setInt("dfs.client.socket-timeout", 5000);
118 
119     // faster failover with cluster.shutdown();fs.close() idiom
120     TEST_UTIL.getConfiguration()
121         .setInt("hbase.ipc.client.connect.max.retries", 1);
122     TEST_UTIL.getConfiguration().setInt(
123         "dfs.client.block.recovery.retries", 1);
124     TEST_UTIL.getConfiguration().setInt(
125       "hbase.ipc.client.connection.maxidletime", 500);
126     TEST_UTIL.getConfiguration().set(CoprocessorHost.WAL_COPROCESSOR_CONF_KEY,
127         SampleRegionWALObserver.class.getName());
128     TEST_UTIL.startMiniDFSCluster(3);
129 
130     conf = TEST_UTIL.getConfiguration();
131     fs = TEST_UTIL.getDFSCluster().getFileSystem();
132   }
133 
134   @AfterClass
135   public static void tearDownAfterClass() throws Exception {
136     TEST_UTIL.shutdownMiniCluster();
137   }
138 
139   /**
140    * A loaded WAL coprocessor won't break existing WAL test cases.
141    */
142   @Test
143   public void testWALCoprocessorLoaded() throws Exception {
144     // test to see whether the coprocessor is loaded or not.
145     FSHLog log = null;
146     try {
147       log = new FSHLog(fs, FSUtils.getRootDir(conf), dir.toString(),
148           HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null, null);
149       WALCoprocessorHost host = log.getCoprocessorHost();
150       Coprocessor c = host.findCoprocessor(SampleRegionWALObserver.class.getName());
151       assertNotNull(c);
152     } finally {
153       if (log != null) {
154         log.close();
155       }
156     }
157   }
158 
159   protected void addEdits(WAL log,
160                           HRegionInfo hri,
161                           HTableDescriptor htd,
162                           int times,
163                           MultiVersionConcurrencyControl mvcc)
164       throws IOException {
165     final byte[] row = Bytes.toBytes("row");
166     for (int i = 0; i < times; i++) {
167       long timestamp = System.currentTimeMillis();
168       WALEdit cols = new WALEdit();
169       cols.add(new KeyValue(row, row, row, timestamp, row));
170       WALKey key = new WALKey(hri.getEncodedNameAsBytes(), htd.getTableName(),
171           WALKey.NO_SEQUENCE_ID, timestamp, WALKey.EMPTY_UUIDS, HConstants.NO_NONCE,
172           HConstants.NO_NONCE, mvcc);
173       log.append(htd, hri, key, cols, true);
174     }
175     log.sync();
176   }
177 
178   /**
179    * helper method to simulate region flush for a WAL.
180    * @param wal
181    * @param regionEncodedName
182    */
183   protected void flushRegion(WAL wal, byte[] regionEncodedName, Set<byte[]> flushedFamilyNames) {
184     wal.startCacheFlush(regionEncodedName, flushedFamilyNames);
185     wal.completeCacheFlush(regionEncodedName);
186   }
187 
188   /**
189    * tests the log comparator. Ensure that we are not mixing meta logs with non-meta logs (throws
190    * exception if we do). Comparison is based on the timestamp present in the wal name.
191    * @throws Exception
192    */
193   @Test 
194   public void testWALComparator() throws Exception {
195     FSHLog wal1 = null;
196     FSHLog walMeta = null;
197     try {
198       wal1 = new FSHLog(fs, FSUtils.getRootDir(conf), dir.toString(),
199           HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null, null);
200       LOG.debug("Log obtained is: " + wal1);
201       Comparator<Path> comp = wal1.LOG_NAME_COMPARATOR;
202       Path p1 = wal1.computeFilename(11);
203       Path p2 = wal1.computeFilename(12);
204       // comparing with itself returns 0
205       assertTrue(comp.compare(p1, p1) == 0);
206       // comparing with different filenum.
207       assertTrue(comp.compare(p1, p2) < 0);
208       walMeta = new FSHLog(fs, FSUtils.getRootDir(conf), dir.toString(),
209           HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null,
210           DefaultWALProvider.META_WAL_PROVIDER_ID);
211       Comparator<Path> compMeta = walMeta.LOG_NAME_COMPARATOR;
212 
213       Path p1WithMeta = walMeta.computeFilename(11);
214       Path p2WithMeta = walMeta.computeFilename(12);
215       assertTrue(compMeta.compare(p1WithMeta, p1WithMeta) == 0);
216       assertTrue(compMeta.compare(p1WithMeta, p2WithMeta) < 0);
217       // mixing meta and non-meta logs gives error
218       boolean ex = false;
219       try {
220         comp.compare(p1WithMeta, p2);
221       } catch (IllegalArgumentException e) {
222         ex = true;
223       }
224       assertTrue("Comparator doesn't complain while checking meta log files", ex);
225       boolean exMeta = false;
226       try {
227         compMeta.compare(p1WithMeta, p2);
228       } catch (IllegalArgumentException e) {
229         exMeta = true;
230       }
231       assertTrue("Meta comparator doesn't complain while checking log files", exMeta);
232     } finally {
233       if (wal1 != null) {
234         wal1.close();
235       }
236       if (walMeta != null) {
237         walMeta.close();
238       }
239     }
240   }
241 
242   /**
243    * On rolling a wal after reaching the threshold, {@link WAL#rollWriter()} returns the
244    * list of regions which should be flushed in order to archive the oldest wal file.
245    * <p>
246    * This method tests this behavior by inserting edits and rolling the wal enough times to reach
247    * the max number of logs threshold. It checks whether we get the "right regions" for flush on
248    * rolling the wal.
249    * @throws Exception
250    */
251   @Test 
252   public void testFindMemStoresEligibleForFlush() throws Exception {
253     LOG.debug("testFindMemStoresEligibleForFlush");
254     Configuration conf1 = HBaseConfiguration.create(conf);
255     conf1.setInt("hbase.regionserver.maxlogs", 1);
256     FSHLog wal = new FSHLog(fs, FSUtils.getRootDir(conf1), dir.toString(),
257         HConstants.HREGION_OLDLOGDIR_NAME, conf1, null, true, null, null);
258     HTableDescriptor t1 =
259         new HTableDescriptor(TableName.valueOf("t1")).addFamily(new HColumnDescriptor("row"));
260     HTableDescriptor t2 =
261         new HTableDescriptor(TableName.valueOf("t2")).addFamily(new HColumnDescriptor("row"));
262     HRegionInfo hri1 =
263         new HRegionInfo(t1.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
264     HRegionInfo hri2 =
265         new HRegionInfo(t2.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
266     // add edits and roll the wal
267     MultiVersionConcurrencyControl mvcc = new MultiVersionConcurrencyControl();
268     try {
269       addEdits(wal, hri1, t1, 2, mvcc);
270       wal.rollWriter();
271       // add some more edits and roll the wal. This would reach the log number threshold
272       addEdits(wal, hri1, t1, 2, mvcc);
273       wal.rollWriter();
274       // with above rollWriter call, the max logs limit is reached.
275       assertTrue(wal.getNumRolledLogFiles() == 2);
276 
277       // get the regions to flush; since there is only one region in the oldest wal, it should
278       // return only one region.
279       byte[][] regionsToFlush = wal.findRegionsToForceFlush();
280       assertEquals(1, regionsToFlush.length);
281       assertEquals(hri1.getEncodedNameAsBytes(), regionsToFlush[0]);
282       // insert edits in second region
283       addEdits(wal, hri2, t2, 2, mvcc);
284       // get the regions to flush, it should still read region1.
285       regionsToFlush = wal.findRegionsToForceFlush();
286       assertEquals(regionsToFlush.length, 1);
287       assertEquals(hri1.getEncodedNameAsBytes(), regionsToFlush[0]);
288       // flush region 1, and roll the wal file. Only last wal which has entries for region1 should
289       // remain.
290       flushRegion(wal, hri1.getEncodedNameAsBytes(), t1.getFamiliesKeys());
291       wal.rollWriter();
292       // only one wal should remain now (that is for the second region).
293       assertEquals(1, wal.getNumRolledLogFiles());
294       // flush the second region
295       flushRegion(wal, hri2.getEncodedNameAsBytes(), t2.getFamiliesKeys());
296       wal.rollWriter(true);
297       // no wal should remain now.
298       assertEquals(0, wal.getNumRolledLogFiles());
299       // add edits both to region 1 and region 2, and roll.
300       addEdits(wal, hri1, t1, 2, mvcc);
301       addEdits(wal, hri2, t2, 2, mvcc);
302       wal.rollWriter();
303       // add edits and roll the writer, to reach the max logs limit.
304       assertEquals(1, wal.getNumRolledLogFiles());
305       addEdits(wal, hri1, t1, 2, mvcc);
306       wal.rollWriter();
307       // it should return two regions to flush, as the oldest wal file has entries
308       // for both regions.
309       regionsToFlush = wal.findRegionsToForceFlush();
310       assertEquals(2, regionsToFlush.length);
311       // flush both regions
312       flushRegion(wal, hri1.getEncodedNameAsBytes(), t1.getFamiliesKeys());
313       flushRegion(wal, hri2.getEncodedNameAsBytes(), t2.getFamiliesKeys());
314       wal.rollWriter(true);
315       assertEquals(0, wal.getNumRolledLogFiles());
316       // Add an edit to region1, and roll the wal.
317       addEdits(wal, hri1, t1, 2, mvcc);
318       // tests partial flush: roll on a partial flush, and ensure that wal is not archived.
319       wal.startCacheFlush(hri1.getEncodedNameAsBytes(), t1.getFamiliesKeys());
320       wal.rollWriter();
321       wal.completeCacheFlush(hri1.getEncodedNameAsBytes());
322       assertEquals(1, wal.getNumRolledLogFiles());
323     } finally {
324       if (wal != null) {
325         wal.close();
326       }
327     }
328   }
329 
330   @Test(expected=IOException.class)
331   public void testFailedToCreateWALIfParentRenamed() throws IOException {
332     final String name = "testFailedToCreateWALIfParentRenamed";
333     FSHLog log = new FSHLog(fs, FSUtils.getRootDir(conf), name, HConstants.HREGION_OLDLOGDIR_NAME,
334         conf, null, true, null, null);
335     long filenum = System.currentTimeMillis();
336     Path path = log.computeFilename(filenum);
337     log.createWriterInstance(path);
338     Path parent = path.getParent();
339     path = log.computeFilename(filenum + 1);
340     Path newPath = new Path(parent.getParent(), parent.getName() + "-splitting");
341     fs.rename(parent, newPath);
342     log.createWriterInstance(path);
343     fail("It should fail to create the new WAL");
344   }
345 
346   /**
347    * Test flush for sure has a sequence id that is beyond the last edit appended.  We do this
348    * by slowing appends in the background ring buffer thread while in foreground we call
349    * flush.  The addition of the sync over HRegion in flush should fix an issue where flush was
350    * returning before all of its appends had made it out to the WAL (HBASE-11109).
351    * see HBASE-11109
352    * @throws IOException
353    */
354   @Test
355   public void testFlushSequenceIdIsGreaterThanAllEditsInHFile() throws IOException {
356     String testName = "testFlushSequenceIdIsGreaterThanAllEditsInHFile";
357     final TableName tableName = TableName.valueOf(testName);
358     final HRegionInfo hri = new HRegionInfo(tableName);
359     final byte[] rowName = tableName.getName();
360     final HTableDescriptor htd = new HTableDescriptor(tableName);
361     htd.addFamily(new HColumnDescriptor("f"));
362     HRegion r = HRegion.createHRegion(hri, TEST_UTIL.getDefaultRootDirPath(),
363       TEST_UTIL.getConfiguration(), htd);
364     HRegion.closeHRegion(r);
365     final int countPerFamily = 10;
366     final MutableBoolean goslow = new MutableBoolean(false);
367     // subclass and doctor a method.
368     FSHLog wal = new FSHLog(FileSystem.get(conf), TEST_UTIL.getDefaultRootDirPath(),
369         testName, conf) {
370       @Override
371       void atHeadOfRingBufferEventHandlerAppend() {
372         if (goslow.isTrue()) {
373           Threads.sleep(100);
374           LOG.debug("Sleeping before appending 100ms");
375         }
376         super.atHeadOfRingBufferEventHandlerAppend();
377       }
378     };
379     HRegion region = HRegion.openHRegion(TEST_UTIL.getConfiguration(),
380       TEST_UTIL.getTestFileSystem(), TEST_UTIL.getDefaultRootDirPath(), hri, htd, wal);
381     EnvironmentEdge ee = EnvironmentEdgeManager.getDelegate();
382     try {
383       List<Put> puts = null;
384       for (HColumnDescriptor hcd: htd.getFamilies()) {
385         puts =
386           TestWALReplay.addRegionEdits(rowName, hcd.getName(), countPerFamily, ee, region, "x");
387       }
388 
389       // Now assert edits made it in.
390       final Get g = new Get(rowName);
391       Result result = region.get(g);
392       assertEquals(countPerFamily * htd.getFamilies().size(), result.size());
393 
394       // Construct a WALEdit and add it a few times to the WAL.
395       WALEdit edits = new WALEdit();
396       for (Put p: puts) {
397         CellScanner cs = p.cellScanner();
398         while (cs.advance()) {
399           edits.add(cs.current());
400         }
401       }
402       // Add any old cluster id.
403       List<UUID> clusterIds = new ArrayList<UUID>();
404       clusterIds.add(UUID.randomUUID());
405       // Now make appends run slow.
406       goslow.setValue(true);
407       for (int i = 0; i < countPerFamily; i++) {
408         final HRegionInfo info = region.getRegionInfo();
409         final WALKey logkey = new WALKey(info.getEncodedNameAsBytes(), tableName,
410             System.currentTimeMillis(), clusterIds, -1, -1, region.getMVCC());
411         wal.append(htd, info, logkey, edits, true);
412       }
413       region.flush(true);
414       // FlushResult.flushSequenceId is not visible here so go get the current sequence id.
415       long currentSequenceId = region.getSequenceId();
416       // Now release the appends
417       goslow.setValue(false);
418       synchronized (goslow) {
419         goslow.notifyAll();
420       }
421       assertTrue(currentSequenceId >= region.getSequenceId());
422     } finally {
423       region.close(true);
424       wal.close();
425     }
426   }
427 
428   @Test
429   public void testSyncRunnerIndexOverflow() throws IOException, NoSuchFieldException,
430       SecurityException, IllegalArgumentException, IllegalAccessException {
431     final String name = "testSyncRunnerIndexOverflow";
432     FSHLog log =
433         new FSHLog(fs, FSUtils.getRootDir(conf), name, HConstants.HREGION_OLDLOGDIR_NAME, conf,
434             null, true, null, null);
435     try {
436       Field ringBufferEventHandlerField = FSHLog.class.getDeclaredField("ringBufferEventHandler");
437       ringBufferEventHandlerField.setAccessible(true);
438       FSHLog.RingBufferEventHandler ringBufferEventHandler =
439           (FSHLog.RingBufferEventHandler) ringBufferEventHandlerField.get(log);
440       Field syncRunnerIndexField =
441           FSHLog.RingBufferEventHandler.class.getDeclaredField("syncRunnerIndex");
442       syncRunnerIndexField.setAccessible(true);
443       syncRunnerIndexField.set(ringBufferEventHandler, Integer.MAX_VALUE - 1);
444       HTableDescriptor htd =
445           new HTableDescriptor(TableName.valueOf("t1")).addFamily(new HColumnDescriptor("row"));
446       HRegionInfo hri =
447           new HRegionInfo(htd.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
448       MultiVersionConcurrencyControl mvcc = new MultiVersionConcurrencyControl();
449       for (int i = 0; i < 10; i++) {
450         addEdits(log, hri, htd, 1, mvcc);
451       }
452     } finally {
453       log.close();
454     }
455   }
456 
457   /**
458    * Test case for https://issues.apache.org/jira/browse/HBASE-16721
459    */
460   @Test (timeout = 30000)
461   public void testUnflushedSeqIdTracking() throws IOException, InterruptedException {
462     final String name = "testSyncRunnerIndexOverflow";
463     final byte[] b = Bytes.toBytes("b");
464 
465     final AtomicBoolean startHoldingForAppend = new AtomicBoolean(false);
466     final CountDownLatch holdAppend = new CountDownLatch(1);
467     final CountDownLatch flushFinished = new CountDownLatch(1);
468     final CountDownLatch putFinished = new CountDownLatch(1);
469 
470     try (FSHLog log =
471         new FSHLog(fs, FSUtils.getRootDir(conf), name, HConstants.HREGION_OLDLOGDIR_NAME, conf,
472             null, true, null, null)) {
473 
474       log.registerWALActionsListener(new WALActionsListener.Base() {
475         @Override
476         public void visitLogEntryBeforeWrite(HTableDescriptor htd, WALKey logKey, WALEdit logEdit)
477             throws IOException {
478           if (startHoldingForAppend.get()) {
479             try {
480               holdAppend.await();
481             } catch (InterruptedException e) {
482               LOG.error(e);
483             }
484           }
485         }
486       });
487 
488       // open a new region which uses this WAL
489       HTableDescriptor htd =
490           new HTableDescriptor(TableName.valueOf("t1")).addFamily(new HColumnDescriptor(b));
491       HRegionInfo hri =
492           new HRegionInfo(htd.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
493 
494       final HRegion region = TEST_UTIL.createLocalHRegion(hri, htd, log);
495       ExecutorService exec = Executors.newFixedThreadPool(2);
496 
497       // do a regular write first because of memstore size calculation.
498       region.put(new Put(b).addColumn(b, b,b));
499 
500       startHoldingForAppend.set(true);
501       exec.submit(new Runnable() {
502         @Override
503         public void run() {
504           try {
505             region.put(new Put(b).addColumn(b, b,b));
506             putFinished.countDown();
507           } catch (IOException e) {
508             LOG.error(e);
509           }
510         }
511       });
512 
513       // give the put a chance to start
514       Threads.sleep(3000);
515 
516       exec.submit(new Runnable() {
517         @Override
518         public void run() {
519           try {
520             Region.FlushResult flushResult = region.flush(true);
521             LOG.info("Flush result:" +  flushResult.getResult());
522             LOG.info("Flush succeeded:" +  flushResult.isFlushSucceeded());
523             flushFinished.countDown();
524           } catch (IOException e) {
525             LOG.error(e);
526           }
527         }
528       });
529 
530       // give the flush a chance to start. Flush should have got the region lock, and
531       // should have been waiting on the mvcc complete after this.
532       Threads.sleep(3000);
533 
534       // let the append to WAL go through now that the flush already started
535       holdAppend.countDown();
536       putFinished.await();
537       flushFinished.await();
538 
539       // check whether flush went through
540       assertEquals("Region did not flush?", 1, region.getStoreFileList(new byte[][]{b}).size());
541 
542       // now check the region's unflushed seqIds.
543       long seqId = log.getEarliestMemstoreSeqNum(hri.getEncodedNameAsBytes());
544       assertEquals("Found seqId for the region which is already flushed",
545           HConstants.NO_SEQNUM, seqId);
546 
547       region.close();
548     }
549   }
550 }