Package com.cloudera.flume.agent.durability

Source Code of com.cloudera.flume.agent.durability.TestNaiveFileWALDeco

/**
* Licensed to Cloudera, Inc. under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  Cloudera, Inc. licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cloudera.flume.agent.durability;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import com.cloudera.flume.agent.FlumeNode;
import com.cloudera.flume.conf.FlumeBuilder;
import com.cloudera.flume.conf.FlumeSpecException;
import com.cloudera.flume.conf.LogicalNodeContext;
import com.cloudera.flume.conf.ReportTestingContext;
import com.cloudera.flume.conf.SinkFactory.SinkDecoBuilder;
import com.cloudera.flume.core.Event;
import com.cloudera.flume.core.EventImpl;
import com.cloudera.flume.core.EventSink;
import com.cloudera.flume.core.EventSinkDecorator;
import com.cloudera.flume.core.EventSource;
import com.cloudera.flume.core.EventUtil;
import com.cloudera.flume.handlers.debug.MemorySinkSource;
import com.cloudera.flume.handlers.debug.NullSink;
import com.cloudera.flume.handlers.endtoend.AckListener;
import com.cloudera.flume.handlers.rolling.ProcessTagger;
import com.cloudera.flume.handlers.rolling.SizeTrigger;
import com.cloudera.flume.handlers.rolling.TimeTrigger;
import com.cloudera.flume.reporter.ReportManager;
import com.cloudera.flume.reporter.aggregator.CounterSink;
import com.cloudera.util.BenchmarkHarness;
import com.cloudera.util.FileUtil;

/**
* This tests the naive file WAL decorator.
*/
public class TestNaiveFileWALDeco {
  static final Logger LOG = Logger.getLogger(NaiveFileWALManager.class
      .getName());

  @Before
  public void setUp() {
    LOG.setLevel(Level.DEBUG);
  }

  /**
   * This test shows that we properly recover and eventually delete walfiles
   * from previous failed sessions.
   */
  @Test
  public void testRecoveredIsDeleted() throws IOException, FlumeSpecException,
      InterruptedException {

    BenchmarkHarness.setupLocalWriteDir();
    File tmp = BenchmarkHarness.tmpdir;

    // file with ack begin, data, and end messages
    File acked = new File("src/data/acked.00000000.20100204-015814430-0800.seq");
    // Assumes the NaiveFileWALManager!
    File writing = new File(new File(tmp, BenchmarkHarness.node
        .getPhysicalNodeName()), "writing");
    writing.mkdirs();

    // Must rename file because that name is in the meta data of the event
    // inside the file!
    // TODO (jon) make this restriction less strict in the future.

    FileUtil.dumbfilecopy(acked, new File(writing,
        "writeahead.00000000.20100204-015814F430-0800.seq"));

    // EventSource src = FlumeBuilder.buildSource("");
    EventSink snk = FlumeBuilder.buildSink(new ReportTestingContext(
        LogicalNodeContext.testingContext()),
        "{ ackedWriteAhead => { ackChecker => counter(\"count\") } }");
    EventSource src = MemorySinkSource.cannedData("foo foo foo ", 5);
    snk.open();
    src.open();
    EventUtil.dumpAll(src, snk);
    src.close();
    snk.close(); // this should block until recovery complete.

    // agent checks for ack registrations.
    BenchmarkHarness.node.getAckChecker().checkAcks();

    CounterSink cnt = (CounterSink) ReportManager.get().getReportable("count");
    // 1032 in file + 5 from silly driver
    assertEquals(1037, cnt.getCount());

    // check to make sure wal file is gone
    assertFalse(new File(new File(tmp, "import"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "writing"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "logged"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "sending"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "sent"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "error"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "done"), acked.getName()).exists());

    BenchmarkHarness.cleanupLocalWriteDir();
  }

  /**
   * This specifically illustrates the case where an acktag doesn't match the
   * name of the wal file. I believe that we should just make the walfile match
   * its acktag an invariant.
   */
  @Test
  public void testMismatchedWalFilenameAckTag() throws IOException,
      FlumeSpecException, InterruptedException {
    // FlumeConfiguration.get().setLong(FlumeConfiguration.AGENT_LOG_MAX_AGE,
    // 50);
    BenchmarkHarness.setupLocalWriteDir();
    File tmp = BenchmarkHarness.tmpdir;

    // file with ack begin, data, and end messages
    File acked = new File("src/data/acked.00000000.20100204-015814430-0800.seq");
    // Assumes the NaiveFileWALManager!
    File writing = new File(new File(tmp, BenchmarkHarness.node
        .getPhysicalNodeName()), "writing");
    writing.mkdirs();

    // /////////////////////
    // This illustrates the problems from the previous test.
    FileUtil.dumbfilecopy(acked, new File(writing, acked.getName()));
    // /////////////////////

    EventSink snk = FlumeBuilder.buildSink(new ReportTestingContext(
        LogicalNodeContext.testingContext()),
        "{ ackedWriteAhead => { ackChecker => counter(\"count\") } }");
    EventSource src = MemorySinkSource.cannedData("foo foo foo ", 5);
    snk.open();
    src.open();
    EventUtil.dumpAll(src, snk);
    src.close();
    snk.close(); // this should block until recovery complete.

    // agent checks for ack registrations.
    BenchmarkHarness.node.getAckChecker().checkAcks();

    CounterSink cnt = (CounterSink) ReportManager.get().getReportable("count");
    // 1032 in file + 5 from silly driverx
    assertEquals(1037, cnt.getCount());

    // check to make sure wal file is gone
    assertFalse(new File(new File(tmp, "import"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "writing"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "logged"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "sending"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "error"), acked.getName()).exists());
    assertFalse(new File(new File(tmp, "done"), acked.getName()).exists());

    // TODO (jon) is this the right behavior? I think assuming no name changes
    // locally is reasonable for now.

    assertTrue(new File(new File(new File(tmp, BenchmarkHarness.node
        .getPhysicalNodeName()), "sent"), acked.getName()).exists());

    BenchmarkHarness.cleanupLocalWriteDir();
  }

  /**
   * This "recovers" a truncated wal file as best as it can and moves it to the
   * err directory. Generally truncated files this would only happen on events
   * such as kill -9, power out, or out of disk space.
   *
   * @throws IOException
   * @throws FlumeSpecException
   * @throws InterruptedException
   */
  @Test
  public void testRecoveredMovesToErr() throws IOException, FlumeSpecException,
      InterruptedException {
    BenchmarkHarness.setupLocalWriteDir();
    File tmp = BenchmarkHarness.tmpdir;

    // Assumes the NaiveFileWALManager!
    // file with ack begin, data and then truncated
    File truncated = new File(
        "src/data/truncated.00000000.20100204-015814430-0800.seq");
    File writing = new File(new File(tmp, BenchmarkHarness.node
        .getPhysicalNodeName()), "writing");

    writing.mkdirs();
    FileUtil.dumbfilecopy(truncated, new File(writing, truncated.getName()));

    EventSink snk = FlumeBuilder.buildSink(new ReportTestingContext(
        LogicalNodeContext.testingContext()),
        "{ ackedWriteAhead => { ackChecker => counter(\"count\") } }");
    EventSource src = MemorySinkSource.cannedData("foo foo foo ", 5);
    snk.open();
    src.open();
    EventUtil.dumpAll(src, snk);
    src.close();
    snk.close(); // this should block until recovery complete.

    CounterSink cnt = (CounterSink) ReportManager.get().getReportable("count");
    // 461 in file before truncated + 5 from silly driver
    assertEquals(466, cnt.getCount());

    // need to trigger ack checks..

    // BenchmarkHarness.mock.ackman.;

    // check to make sure wal file is gone
    File nodedir = new File(tmp, BenchmarkHarness.node.getPhysicalNodeName());

    assertFalse(new File(new File(nodedir, "import"), truncated.getName())
        .exists());
    assertFalse(new File(new File(nodedir, "writing"), truncated.getName())
        .exists());
    assertFalse(new File(new File(nodedir, "logged"), truncated.getName())
        .exists());
    assertFalse(new File(new File(nodedir, "sending"), truncated.getName())
        .exists());
    assertFalse(new File(new File(nodedir, "sent"), truncated.getName())
        .exists());
    assertTrue(new File(new File(nodedir, "error"), truncated.getName())
        .exists());
    assertFalse(new File(new File(nodedir, "done"), truncated.getName())
        .exists());

    BenchmarkHarness.cleanupLocalWriteDir();
  }

  /**
   * An old implementation could deadlock if append was called before open. This
   * test tests this issue to an extent - we don't test concurrent append-> open
   * as it requires injecting code into the implementation of append.
   */
  @Test
  public void testAppendBeforeOpen() throws InterruptedException {
    final NaiveFileWALDeco d = new NaiveFileWALDeco(LogicalNodeContext
        .testingContext(), new NullSink(), new NaiveFileWALManager(new File(
        "/tmp")), new SizeTrigger(0, null), new AckListener.Empty(), 1000000);
    final CountDownLatch cdl1 = new CountDownLatch(1);
    new Thread() {
      public void run() {
        try {
          d.append(new EventImpl("hello".getBytes()));
          d.open();
          cdl1.countDown();
        } catch (IOException e) {
          LOG.error("Saw IOException in testAppendBeforeOpen", e);
        } catch (IllegalStateException e) {
          // Expected illegal state exception due to not being open
          cdl1.countDown();
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }.start();
    assertTrue("Latch not fired", cdl1.await(5000, TimeUnit.MILLISECONDS));
  }

  /**
   * Test the case where data with out ack tags is being sent to the wal deco.
   */
  @Test
  public void testBadRegistererAppend() throws InterruptedException {

    final NaiveFileWALDeco d = new NaiveFileWALDeco(LogicalNodeContext
        .testingContext(), new NullSink(), new NaiveFileWALManager(new File(
        "/tmp")), new SizeTrigger(0, null), new AckListener.Empty(), 1000000);

    final CountDownLatch cdl1 = new CountDownLatch(1);
    new Thread() {
      public void run() {
        try {
          d.append(new EventImpl("hello".getBytes()));
          d.open();
          cdl1.countDown();
        } catch (IOException e) {
          LOG.error("Saw IOException in testAppendBeforeOpen", e);
        } catch (IllegalStateException e) {
          // Expected illegal state exception due to not being open
          cdl1.countDown();
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }.start();
    assertTrue("Latch not fired", cdl1.await(5000, TimeUnit.MILLISECONDS));
  }

  @Test(expected = IOException.class)
  public void testExceptionThreadHandoff() throws IOException,
      InterruptedException {
    try {
      BenchmarkHarness.setupLocalWriteDir();
      Event e = new EventImpl(new byte[0]);
      EventSink snk = new EventSink.Base() {
        @Override
        public void append(Event e) throws IOException {
          throw new IOException("mock ioe");
        }
      };

      FlumeNode node = FlumeNode.getInstance();
      EventSinkDecorator<EventSink> deco = new NaiveFileWALDeco(
          LogicalNodeContext.testingContext(), snk, node.getWalManager(),
          new TimeTrigger(new ProcessTagger(), 1000), node.getAckChecker()
              .getAgentAckQueuer(), 1000);

      deco.open();
      deco.append(e);
      deco.close();
    } catch (IOException e) {
      throw e;
    } finally {
      BenchmarkHarness.cleanupLocalWriteDir();
    }
  }

  @Test(expected = IllegalArgumentException.class)
  public void testBadE2eBuilderArgs() {
    SinkDecoBuilder b = NaiveFileWALDeco.builderEndToEndDir();
    b.build(LogicalNodeContext.testingContext(), "foo", "bar");
  }

}
TOP

Related Classes of com.cloudera.flume.agent.durability.TestNaiveFileWALDeco

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.