se.sics.caracaldb.global.LUTUpdate.java Source code

Java tutorial

Introduction

Here is the source code for se.sics.caracaldb.global.LUTUpdate.java

Source

/*
 * This file is part of the CaracalDB distributed storage system.
 *
 * Copyright (C) 2009 Swedish Institute of Computer Science (SICS) 
 * Copyright (C) 2009 Royal Institute of Technology (KTH)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package se.sics.caracaldb.global;

import com.google.common.base.Optional;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Set;
import java.util.logging.Level;
import org.javatuples.Triplet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.sics.caracaldb.Key;
import se.sics.caracaldb.View;
import se.sics.caracaldb.replication.linearisable.ViewChange;
import se.sics.caracaldb.utils.CustomSerialisers;
import se.sics.kompics.address.Address;
import se.sics.kompics.network.netty.serialization.SpecialSerializers;

/**
 *
 * @author sario
 */
public class LUTUpdate implements Maintenance {

    public static final Logger LOG = LoggerFactory.getLogger(LUTUpdate.class);

    private final long previousVersion;
    public final long version;
    private final Action[] diff;

    private ByteBuf serializationCache = null;

    public LUTUpdate(long previousVersion, long version, Action[] diff) {
        this.previousVersion = previousVersion;
        this.version = version;
        this.diff = diff;
    }

    public void apply(LookupTable lut, Callbacks call) {
        lut.versionId = version;
        for (Action a : diff) {
            a.apply(lut, call);
        }
    }

    public boolean applicable(LookupTable lut) {
        return lut.versionId == previousVersion;
    }

    public long dependsOn() {
        return previousVersion;
    }

    ImmutableSet<Address> joiners() {
        ImmutableSet.Builder<Address> joiners = ImmutableSet.builder();
        for (Action a : diff) {
            if (a instanceof PutHost) {
                PutHost ph = (PutHost) a;
                if (ph.addr != null) {
                    joiners.add(ph.addr);
                }
            }
        }
        return joiners.build();
    }

    public byte[] serialise() {
        if (serializationCache == null) {
            ByteBuf buf = Unpooled.buffer();
            serialise(buf);
            buf.release();
        }

        byte[] data = new byte[serializationCache.readableBytes()];
        serializationCache.getBytes(0, data);
        return data;
    }

    public void serialise(ByteBuf buf) {
        if (serializationCache == null) {
            serializationCache = Unpooled.buffer();
            serializationCache.writeLong(previousVersion);
            serializationCache.writeLong(version);

            serializationCache.writeInt(diff.length);
            for (Action a : diff) {
                a.serialise(serializationCache);
            }
        }
        buf.writeBytes(serializationCache, 0, serializationCache.readableBytes());
    }

    public static LUTUpdate deserialise(byte[] bytes) throws InstantiationException, IllegalAccessException {
        ByteBuf buf = Unpooled.wrappedBuffer(bytes);

        LUTUpdate lutu = deserialise(buf);

        buf.release();

        return lutu;
    }

    public static LUTUpdate deserialise(ByteBuf buf) throws InstantiationException, IllegalAccessException {
        long previousVersion = buf.readLong();
        long version = buf.readLong();

        int numActions = buf.readInt();
        Action[] actions = new Action[numActions];
        for (int i = 0; i < numActions; i++) {
            actions[i] = Action.deserialise(buf);
        }

        return new LUTUpdate(previousVersion, version, actions);
    }

    public static abstract class Action {

        public static final ImmutableBiMap<Byte, Class<? extends Action>> TYPES;

        static {
            HashBiMap<Byte, Class<? extends Action>> types = HashBiMap.create();

            types.put((byte) 1, PutHost.class);
            types.put((byte) 2, PutReplicationSet.class);
            types.put((byte) 3, PutReplicationGroup.class);
            types.put((byte) 4, CreateSchema.class);
            types.put((byte) 5, DropSchema.class);

            TYPES = ImmutableBiMap.copyOf(types);
        }

        public byte code() {
            return TYPES.inverse().get(this.getClass());
        }

        public abstract void apply(LookupTable lut, Callbacks call);

        public abstract void serialise(ByteBuf buf);

        public abstract void fill(ByteBuf buf);

        public static Action deserialise(ByteBuf buf) throws InstantiationException, IllegalAccessException {
            byte type = buf.readByte();
            Class<? extends Action> clazz = TYPES.get(type);
            Action a = clazz.newInstance();
            a.fill(buf);
            return a;
        }
    }

    public static class CreateSchema extends Action {

        private SchemaData.SingleSchema schema;

        CreateSchema() {
        }

        public CreateSchema(byte[] id, String name, ImmutableMap<String, String> metaData) {
            schema = new SchemaData.SingleSchema(ByteBuffer.wrap(id), name, metaData);
        }

        public CreateSchema(SchemaData.SingleSchema schema) {
            this.schema = schema;
        }

        @Override
        public void apply(LookupTable lut, Callbacks call) {
            lut.schemas().schemaNames.put(schema.id, schema.name);
            lut.schemas().schemaIDs.put(schema.name, schema.id);
            lut.schemas().metaData.put(schema.id, schema.meta);
        }

        @Override
        public void serialise(ByteBuf buf) {
            buf.writeByte(code());
            SchemaData.serialiseSchema(buf, schema.id, schema.name, schema.meta);
        }

        @Override
        public void fill(ByteBuf buf) {
            schema = SchemaData.deserialiseSchema(buf);
        }
    }

    public static class DropSchema extends Action {

        private ByteBuffer id;

        DropSchema() {

        }

        DropSchema(byte[] id) {
            this(ByteBuffer.wrap(id));
        }

        DropSchema(ByteBuffer id) {
            this.id = id;
        }

        @Override
        public void apply(LookupTable lut, Callbacks call) {
            String name = lut.schemas().schemaNames.remove(id);
            if (name != null) {
                lut.schemas().schemaIDs.remove(name);
                lut.schemas().metaData.remove(id);
            }
        }

        @Override
        public void serialise(ByteBuf buf) {
            buf.writeByte(code());
            buf.writeInt(buf.array().length);
            buf.writeBytes(buf.array());
        }

        @Override
        public void fill(ByteBuf buf) {
            int l = buf.readInt();
            byte[] idB = new byte[l];
            buf.readBytes(idB);
            this.id = ByteBuffer.wrap(idB);
        }

    }

    public static class PutHost extends Action {

        private int id;
        private Address addr;

        PutHost() {

        }

        public PutHost(int id, Address addr) {
            this.id = id;
            this.addr = addr;
        }

        @Override
        public void apply(LookupTable lut, Callbacks call) {
            ArrayList<Address> hosts = lut.hosts();
            while (id >= hosts.size()) {
                hosts.add(null); // just increase the size until it fits (will probably be filled by later ops)
            }
            hosts.set(id, addr);
        }

        @Override
        public void serialise(ByteBuf buf) {
            buf.writeByte(code());
            buf.writeInt(id);
            SpecialSerializers.AddressSerializer.INSTANCE.toBinary(addr, buf);
        }

        @Override
        public void fill(ByteBuf buf) {
            id = buf.readInt();
            addr = (Address) SpecialSerializers.AddressSerializer.INSTANCE.fromBinary(buf, Optional.absent());
        }

    }

    public static class PutReplicationSet extends Action {

        private int id;
        private int version;
        private Integer[] hosts;

        PutReplicationSet() {

        }

        public PutReplicationSet(int id, int version, Integer[] hosts) {
            this.id = id;
            this.version = version;
            this.hosts = hosts;
        }

        @Override
        public void apply(LookupTable lut, Callbacks call) {
            int posOld = LookupTable.positionInSet(lut.replicationSets().get(id), call.getAddressId());
            ArrayList<Integer> rsvs = lut.replicationSetVersions();
            ArrayList<Integer[]> rss = lut.replicationSets();
            if (rsvs.size() != rss.size()) {
                LOG.error("Replication Sets and their Versions seems to be out of sync! (" + rss.size() + ", "
                        + rsvs.size() + ")...fixing...");
                // It's fixable at this point...but the more code depends on this, the less likely it is that the fix is actually correct
                while (rsvs.size() > rss.size()) {
                    rss.add(new Integer[0]);
                }
                while (rss.size() > rsvs.size()) {
                    rsvs.add(0);
                }
            }
            // Now that they are definitely of equal size, we just have to check one
            while (id >= rss.size()) {
                rss.add(new Integer[0]); // just increase the size until it fits (will probably be filled by later ops)
                rsvs.add(0);
                //TODO Also add items to the list of reclaimable entries once that code is merged
            }
            rss.set(id, hosts);
            rsvs.set(id, version);
            int posNew = LookupTable.positionInSet(hosts, call.getAddressId());
            if ((posNew >= 0) && (posOld < 0)) { // new in the set
                Set<Key> vNodes = lut.getVirtualNodesFor(id);
                for (Key k : vNodes) {
                    call.startVNode(k);
                }
            }
            if ((posNew < 0) && (posOld >= 0)) { // removed from set
                Set<Key> vNodes = lut.getVirtualNodesFor(id);
                for (Key k : vNodes) {
                    call.killVNode(k);
                }
            }
            if ((posNew >= 0) && (posOld >= 0)) { // no change for me, but set membership changed
                Set<Key> vNodes = lut.getVirtualNodesFor(id);
                for (Key k : vNodes) {
                    View v = new View(ImmutableSortedSet.copyOf(lut.getHosts(id)), version);
                    try {
                        call.reconf(k, new ViewChange(v, v.members.size() / 2 + 1, lut.getResponsibility(k)));
                    } catch (LookupTable.NoSuchSchemaException ex) {
                        LOG.error("Could not find responsible nodes in a schema for key {}! Not reconfiguring...",
                                k);
                    }
                }
            }
        }

        @Override
        public void serialise(ByteBuf buf) {
            buf.writeByte(code());
            buf.writeInt(id);
            buf.writeInt(version);
            buf.writeInt(hosts.length);
            for (Integer hostId : hosts) {
                buf.writeInt(hostId);
            }
        }

        @Override
        public void fill(ByteBuf buf) {
            id = buf.readInt();
            version = buf.readInt();
            int numHosts = buf.readInt();
            hosts = new Integer[numHosts];
            for (int i = 0; i < numHosts; i++) {
                hosts[i] = buf.readInt();
            }
        }

    }

    public static class PutReplicationGroup extends Action {

        private Key key;
        private Integer replicationSet;

        PutReplicationGroup() {

        }

        public PutReplicationGroup(Key key, Integer replicationSet) {
            this.key = key;
            this.replicationSet = replicationSet;
        }

        @Override
        public void apply(LookupTable lut, Callbacks call) {
            Integer[] oldHosts = lut.getReplicationGroup(key);

            lut.virtualHostsPut(key, replicationSet);

            if (oldHosts == null) {
                call.startVNode(key); // newly added
            } else {
                int posOld = LookupTable.positionInSet(oldHosts, call.getAddressId());
                if ((replicationSet == null) && (posOld >= 0)) {
                    call.killVNode(key); // totally removed vnode
                } else {
                    Integer[] newHosts = lut.replicationSets().get(replicationSet);
                    int posNew = LookupTable.positionInSet(newHosts, call.getAddressId());
                    if ((posNew >= 0) && (posOld < 0)) { // new in the set                
                        call.startVNode(key);
                    }
                    if ((posNew < 0) && (posOld >= 0)) { // removed from set                
                        call.killVNode(key);
                    }
                    if ((posNew >= 0) && (posOld >= 0)) { // no change for me, but set membership changed
                        try {
                            View v = new View(ImmutableSortedSet.copyOf(lut.getResponsibles(key)),
                                    lut.replicationSetVersions().get(replicationSet));
                            call.reconf(key,
                                    new ViewChange(v, v.members.size() / 2 + 1, lut.getResponsibility(key)));
                        } catch (LookupTable.NoSuchSchemaException ex) {
                            LOG.error(
                                    "Could not find responsible nodes in a schema for key {}! Not reconfiguring...",
                                    key);
                        }
                    }
                }
            }
        }

        @Override
        public void serialise(ByteBuf buf) {
            buf.writeByte(code());
            CustomSerialisers.serialiseKey(key, buf);
            if (replicationSet == null) {
                buf.writeBoolean(false);
            } else {
                buf.writeBoolean(true);
                buf.writeInt(replicationSet);
            }
        }

        @Override
        public void fill(ByteBuf buf) {
            key = CustomSerialisers.deserialiseKey(buf);
            if (buf.readBoolean()) {
                replicationSet = buf.readInt();
            } else {
                replicationSet = null;
            }
        }
    }

    public static interface Callbacks {

        public Address getAddress();

        public Integer getAddressId();

        public void killVNode(Key k);

        public void startVNode(Key k);

        public void reconf(Key key, ViewChange change);
    }
}