package com.neocoretechs.bigsack.io;
import java.io.IOException;
import java.nio.channels.Channel;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import com.neocoretechs.bigsack.io.cluster.IOWorkerInterface;
import com.neocoretechs.bigsack.io.pooled.Datablock;
import com.neocoretechs.bigsack.io.pooled.GlobalDBIO;
import com.neocoretechs.bigsack.io.request.IoRequestInterface;
/**
* This is the primordial IO worker. It exists in standalone mode as the primary threaded worker accessing
* a particular tablespace from the requests placed in the ArrayBlockingQueue. It also exists in cluster mode
* as the thread behind the TCPWorker which is derived from it. In cluster mode the WorkerRequestProcessor is an
* additional thread that actually uses the IOWorker queue to queue requests back to the TCPWorker, an
* intentional design compromise.
* The ioUnit is an IoInterface that connects to the underlying raw store, outside of the page/block pool/buffer
* and provides the low level 'fread','fwrite','fseek' etc functions.
* The request queue is initialized with a capacity of 1024 requests with 'put' operations blocking
* until a number below that are served.
* @author jg
*
*/
public class IOWorker implements Runnable, IoInterface, IOWorkerInterface {
private static final boolean DEBUG = false;
private static final int QUEUEMAX = 1024;
private IoInterface ioUnit;
private long nextFreeBlock = -1L;
private BlockingQueue<IoRequestInterface> requestQueue;
public boolean shouldRun = true;
private int tablespace; // 0-7
private String DBName;
private String remoteDBName = null;
/**
* Default Constructor. The purpose is to allow a cluster master to boot without
* reference to the locally mapped IO subsystem. In effect, the local IO operations
* are replaced with traffic to remote nodes instead. The requests themselves are similar
* but network bound ones add additional garnish
*/
public IOWorker() {
requestQueue = new ArrayBlockingQueue<IoRequestInterface>(QUEUEMAX, true); // true maintains FIFO order
}
/**
* Create an IOWorker for the local store with the default of logs and tablespaces under
* the single directory structure in parameter 1
* @param name
* @param tablespace
* @param L3cache
* @throws IOException
*/
public IOWorker(String name, int tablespace, int L3cache) throws IOException {
this.DBName = name;
this.tablespace = tablespace;
requestQueue = new ArrayBlockingQueue<IoRequestInterface>(QUEUEMAX, true);
switch (L3cache) {
case 0 :
ioUnit = new MmapIO();
break;
case 1 :
ioUnit = new FileIO();
break;
default:
throw new IOException("Unknown level 3 cache type, repair configuration file");
}
if (!ioUnit.Fopen(DBName + "." + String.valueOf(tablespace), true))
throw new IOException("IOWorker Cannot create tablespace "+tablespace+" using db:"+name);
}
/**
* Create an IOWorker for the local store with the separation of logs and tablespaces under
* the 2 directory structures in parameter 1 and 2
* @param name
* @param tablespace
* @param L3cache
* @throws IOException
*/
public IOWorker(String name, String remote, int tablespace, int L3cache) throws IOException {
this.DBName = name;
this.remoteDBName = remote;
this.tablespace = tablespace;
requestQueue = new ArrayBlockingQueue<IoRequestInterface>(QUEUEMAX, true);
switch (L3cache) {
case 0 :
ioUnit = new MmapIO();
break;
case 1 :
ioUnit = new FileIO();
break;
default:
throw new IOException("Unknown level 3 cache type, repair configuration file");
}
if (!ioUnit.Fopen(remoteDBName + "." + String.valueOf(tablespace), true))
throw new IOException("IOWorker Cannot create tablespace "+tablespace+" for "+name+" using remote "+remote);
}
public long getNextFreeBlock() {
return nextFreeBlock;
}
public void setNextFreeBlock(long nextFreeBlock) {
this.nextFreeBlock = nextFreeBlock;
}
public BlockingQueue<IoRequestInterface> getRequestQueue() {
return requestQueue;
}
/**
* Queue a request on this worker, the request is assumed to be on this tablespace
* once the request is processed, a notify is issued on the request object
* @param irf
*/
public void queueRequest(IoRequestInterface irf) {
irf.setIoInterface(ioUnit);
irf.setTablespace(tablespace);
if( DEBUG ) {
System.out.println("Adding request "+irf+" size:"+requestQueue.size());
}
try {
requestQueue.put(irf);
} catch (InterruptedException e) {
return; // executor shutdown in effect most likely
}
}
public int getRequestQueueLength() { return requestQueue.size(); }
@Override
public void run() {
while(shouldRun) {
try {
IoRequestInterface iori = requestQueue.take();
iori.process();
} catch (InterruptedException e) {
// most likely a shutdown request
// quit the processing thread
return;
} catch (IOException e) {
System.out.println("IOWorker Request queue exception "+e+" in db "+DBName+" tablespace "+tablespace);
e.printStackTrace();
}
}
}
@Override
public boolean Fopen(String fname, boolean create) throws IOException {
synchronized(ioUnit) {
if (!ioUnit.Fopen(fname + "." + String.valueOf(tablespace), create))
return false;
return true;
}
}
@Override
public void Fopen() throws IOException {
Fopen(DBName, false);
}
@Override
public void Fclose() throws IOException {
synchronized(ioUnit) {
ioUnit.Fclose();
}
}
/**
* Return the position as a real block number
*/
@Override
public long Ftell() throws IOException {
return ioUnit.Ftell();
}
/**
* Seek the real offset, not virtual
*/
@Override
public void Fseek(long offset) throws IOException {
if( DEBUG )
System.out.println("IOWorker Fseek "+offset);
synchronized(ioUnit) {
ioUnit.Fseek(offset);
}
}
@Override
public long Fsize() throws IOException {
if( DEBUG )
System.out.println("IOWorker fsize "+ioUnit.Fsize());
synchronized(ioUnit) {
return ioUnit.Fsize();
}
}
@Override
public void Fset_length(long newlen) throws IOException {
synchronized(ioUnit) {
if( DEBUG )
System.out.println("IOUnit Fset_length "+newlen);
ioUnit.Fset_length(newlen);
}
}
@Override
public void Fforce() throws IOException {
if( DEBUG )
System.out.println("IOWorker force ");
synchronized(ioUnit) {
ioUnit.Fforce();
}
}
@Override
public void Fwrite(byte[] obuf) throws IOException {
if( DEBUG )
System.out.println("IOWorker fwrite "+obuf.length+" @"+Ftell());
synchronized(ioUnit) {
ioUnit.Fwrite(obuf);
}
}
@Override
public void Fwrite(byte[] obuf, int osiz) throws IOException {
if( DEBUG )
System.out.println("IOWorker fwrite "+obuf.length+" @"+Ftell());
synchronized(ioUnit) {
ioUnit.Fwrite(obuf, osiz);
}
}
@Override
public void Fwrite_int(int obuf) throws IOException {
synchronized(ioUnit) {
ioUnit.Fwrite_int(obuf);
}
}
@Override
public void Fwrite_long(long obuf) throws IOException {
synchronized(ioUnit) {
ioUnit.Fwrite_long(obuf);
}
}
@Override
public void Fwrite_short(short obuf) throws IOException {
synchronized(ioUnit) {
ioUnit.Fwrite_short(obuf);
}
}
@Override
public int Fread(byte[] b, int osiz) throws IOException {
if( DEBUG )
System.out.println("IOWorker fread "+osiz+" @"+Ftell());
synchronized(ioUnit) {
return ioUnit.Fread(b, osiz);
}
}
@Override
public int Fread(byte[] b) throws IOException {
if( DEBUG )
System.out.println("IOWorker fread "+b.length+" @"+Ftell());
synchronized(ioUnit) {
return ioUnit.Fread(b);
}
}
@Override
public long Fread_long() throws IOException {
if( DEBUG )
System.out.println("IOWorker fread_long @"+Ftell());
synchronized(ioUnit) {
return ioUnit.Fread_long();
}
}
@Override
public int Fread_int() throws IOException {
if( DEBUG )
System.out.println("IOWorker fread_int @"+Ftell());
synchronized(ioUnit) {
return ioUnit.Fread_int();
}
}
@Override
public short Fread_short() throws IOException {
if( DEBUG )
System.out.println("IOWorker fread_short @"+Ftell());
synchronized(ioUnit) {
return ioUnit.Fread_short();
}
}
@Override
public String FTread() throws IOException {
synchronized(ioUnit) {
return ioUnit.FTread();
}
}
@Override
public void FTwrite(String ins) throws IOException {
synchronized(ioUnit) {
ioUnit.FTwrite(ins);
}
}
@Override
public void Fdelete() {
synchronized(ioUnit) {
ioUnit.Fdelete();
}
}
@Override
public String Fname() {
return DBName;
}
@Override
public boolean isopen() {
synchronized(ioUnit) {
return ioUnit.isopen();
}
}
@Override
public boolean iswriteable() {
synchronized(ioUnit) {
return ioUnit.iswriteable();
}
}
@Override
public boolean isnew() {
synchronized(ioUnit) {
return ioUnit.isnew();
}
}
@Override
public Channel getChannel() {
synchronized(ioUnit) {
return ioUnit.getChannel();
}
}
}