/*
* Copyright 2013 Jive Software, Inc
*
* Licensed 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.jivesoftware.os.amza.service.storage.binary;
import com.jivesoftware.os.amza.api.filer.IAppendOnly;
import com.jivesoftware.os.amza.api.filer.UIO;
import com.jivesoftware.os.amza.api.stream.RowType;
import com.jivesoftware.os.amza.api.wal.WALWriter;
import com.jivesoftware.os.amza.api.filer.HeapFiler;
import com.jivesoftware.os.amza.api.IoStats;
import com.jivesoftware.os.amza.service.storage.filer.WALFiler;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.list.array.TLongArrayList;
import java.io.IOException;
public class BinaryRowWriter implements WALWriter {
private final IAppendOnly appendOnly;
public BinaryRowWriter(WALFiler filer) throws IOException {
this.appendOnly = filer.appender();
}
@Override
public int write(IoStats ioStats,
long txId,
RowType rowType,
int estimatedNumberOfRows,
int estimatedSizeInBytes,
RawRows rows,
IndexableKeys indexableKeys,
TxKeyPointerFpStream stream,
boolean addToLeapCount,
boolean hardFsyncBeforeLeapBoundary) throws Exception {
byte[] lengthBuffer = new byte[8];
HeapFiler memoryFiler = new HeapFiler((estimatedNumberOfRows * (4 + 1 + 8 + 4)) + estimatedSizeInBytes);
TLongArrayList offsets = new TLongArrayList();
rows.consume(row -> {
offsets.add(memoryFiler.getFilePointer());
int length = (1 + 8) + row.length;
UIO.writeInt(memoryFiler, length, "length", lengthBuffer);
UIO.writeByte(memoryFiler, rowType.toByte(), "rowType");
UIO.writeLong(memoryFiler, txId, "txId", lengthBuffer);
memoryFiler.write(row, 0, row.length);
UIO.writeInt(memoryFiler, length, "length", lengthBuffer);
return true;
});
long l = memoryFiler.length();
long startFp;
ioStats.wrote.add(l);
synchronized (appendOnly.lock()) {
startFp = appendOnly.length();
appendOnly.write(memoryFiler.leakBytes(), 0, (int) l);
appendOnly.flush(false); // TODO expose to config
}
TLongIterator iter = offsets.iterator();
indexableKeys.consume((prefix, key, value, valueTimestamp, valueTombstones, valueVersion)
-> stream.stream(txId, prefix, key, value, valueTimestamp, valueTombstones, valueVersion, startFp + iter.next()));
return offsets.size();
}
@Override
public long writeHighwater(IoStats ioStats, byte[] row) throws Exception {
return writeRowInternal(ioStats, row, RowType.highwater);
}
@Override
public long writeSystem(IoStats ioStats, byte[] row) throws Exception {
return writeRowInternal(ioStats, row, RowType.system);
}
private long writeRowInternal(IoStats ioStats, byte[] row, RowType type) throws Exception {
long[] fps = new long[1];
write(ioStats,
-1L,
type,
1,
row.length,
stream -> stream.stream(row),
stream -> stream.stream(null, null, null, -1, false, -1),
(txId, prefix, key, value, valueTimestamp, valueTombstoned, valueVerion, fp) -> {
fps[0] = fp;
return true;
},
true,
false);
return fps[0];
}
@Override
public long getEndOfLastRow() throws Exception {
synchronized (appendOnly.lock()) {
return appendOnly.length();
}
}
public long length() throws IOException {
return appendOnly.length();
}
public void flush(boolean fsync) throws IOException {
appendOnly.flush(fsync);
}
public void close() throws IOException {
appendOnly.close();
}
}