package org.apache.cassandra.db.hints; /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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. * */ import java.io.File; import java.io.IOError; import java.io.IOException; import java.net.InetAddress; import java.util.zip.CRC32; import java.util.zip.Checksum; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.RowMutation; import org.apache.cassandra.dht.Token; import org.apache.cassandra.io.DeletionService; import org.apache.cassandra.io.util.BufferedRandomAccessFile; import org.apache.cassandra.io.util.DataOutputBuffer; import org.apache.log4j.Logger; /** * Hints log segment stores all hinted writes for single endpoint. * Hint log will swicth to new file as soon as hints delivery is started and segment reached its size constraint * * @author Oleg Anastasyev<oa@hq.one.lv> * */ public class HintLogSegment { private static final Logger logger = Logger.getLogger(HintLogSegment.class); /** * This is null until a write is done */ private BufferedRandomAccessFile logWriter; private HintLogHeader header; /** * Endpoint's token, for which this segment stores hinted mutations */ private String token; /** * File this context stores data in */ private String file; public HintLogSegment(String token) { this.header = new HintLogHeader(); this.token = token; } HintLogSegment(String token, String logFile) { logger.info("Opening hint log segment " + logFile); String headerPath = HintLogHeader.getHeaderPathFromSegmentPath(logFile); try { this.header = HintLogHeader.readCommitLogHeader(headerPath); } catch (IOException ioe) { this.header = new HintLogHeader(); logger.info(headerPath + " incomplete, missing or corrupt. Everything is ok, don't panic. HintLog will be replayed from the beginning"); logger.debug("exception was", ioe); } this.token = token; this.file = logFile; } private void createWriter() throws IOError { assert isEmpty(); this.file = DatabaseDescriptor.getHintLogDirectory() + File.separator + "Hints-" + token.toString() + "-" + System.currentTimeMillis() + ".log"; logger.info("Creating new hint log segment " + file); try { logWriter = createWriter(file); logWriter.setSkipCache(true); writeHeader(); } catch (IOException e) { throw new IOError(e); } } public void writeHeader() throws IOException { HintLogHeader.writeCommitLogHeader(header, getHeaderPath() ); } private static BufferedRandomAccessFile createWriter(String file) throws IOException { return new BufferedRandomAccessFile(file, "rw", 128 * 1024); } public BufferedRandomAccessFile createReader() throws IOException { return new BufferedRandomAccessFile(file, "r", 128 * 1024); } public void write(byte[] serializedRow) throws IOException { if (logWriter == null) { assert isEmpty(); createWriter(); } long currentPosition = -1L; try { currentPosition = logWriter.getFilePointer(); // write mutation, w/ checksum // TODO ensure do we really want this instanceof here specfically for hints here Checksum checkum = new CRC32(); logWriter.writeLong(serializedRow.length); logWriter.write(serializedRow); checkum.update(serializedRow, 0, serializedRow.length); logWriter.writeLong(checkum.getValue()); } catch (IOException e) { if (currentPosition != -1) logWriter.seek(currentPosition); throw e; } } public void sync() throws IOException { if (logWriter!=null) logWriter.sync(); } public HintLogHeader getHeader() { return header; } public boolean isFullyReplayed() { assert file != null; return new File(file).length() <=header.getPosition(); } public boolean isEmpty() { return file==null ; } public String getPath() { return file; } public String getHeaderPath() { return HintLogHeader.getHeaderPathFromSegment(this); } public long length() { if (file==null) return 0l; if (logWriter!=null) try { return logWriter.length(); } catch (IOException e) { throw new IOError(e); } return new File(file).length(); } public void close() { if (logWriter==null) return; try { logWriter.close(); } catch (IOException e) { throw new IOError(e); } } public void delete() { assert getPath() != null; DeletionService.submitDelete(getHeaderPath()); DeletionService.submitDelete(getPath()); } @Override public String toString() { return "HintLogSegment(" + (isEmpty() ? "empty" : file ) + ')'; } /** * @return primary endpoint token in ring this hintlog was created for */ public String getToken() { return token; } }