Package com.netflix.astyanax.contrib.dualwrites

Source Code of com.netflix.astyanax.contrib.dualwrites.DualWritesKeyspace$SimpleSyncExec

package com.netflix.astyanax.contrib.dualwrites;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.util.concurrent.ListenableFuture;
import com.netflix.astyanax.AstyanaxConfiguration;
import com.netflix.astyanax.ColumnMutation;
import com.netflix.astyanax.Execution;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.SerializerPackage;
import com.netflix.astyanax.connectionpool.ConnectionPool;
import com.netflix.astyanax.connectionpool.Operation;
import com.netflix.astyanax.connectionpool.OperationResult;
import com.netflix.astyanax.connectionpool.TokenRange;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.connectionpool.exceptions.OperationException;
import com.netflix.astyanax.cql.CqlStatement;
import com.netflix.astyanax.ddl.KeyspaceDefinition;
import com.netflix.astyanax.ddl.SchemaChangeResult;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.partitioner.Partitioner;
import com.netflix.astyanax.query.ColumnFamilyQuery;
import com.netflix.astyanax.retry.RetryPolicy;
import com.netflix.astyanax.serializers.UnknownComparatorException;

/**
* Main class that orchistrates all the dual writes. It wraps the 2 keyspaces (source and destination)
* and appropriately forwards reads and writes to it as dictated by updates using the {@link DualWritesUpdateListener}
*
* Note that if dual writes are enabled then the writes are sent to the {@link DualWritesMutationBatch} or {@link DualWritesColumnMutation}
* classes appropriately.
*
* The reads are always served from the primary data source.
*
* @author poberai
*
*/
public class DualWritesKeyspace implements Keyspace, DualWritesUpdateListener {

    private static final Logger Logger = LoggerFactory.getLogger(DualWritesKeyspace.class);
   
  private final AtomicReference<KeyspacePair> ksPair = new AtomicReference<KeyspacePair>(null);
    private final AtomicBoolean dualWritesEnabled = new AtomicBoolean(false);
 
    private final DualWritesStrategy executionStrategy;
   
  public DualWritesKeyspace(DualKeyspaceMetadata dualKeyspaceSetup,  
                            Keyspace primaryKS, Keyspace secondaryKS,
                            DualWritesStrategy execStrategy) {
    ksPair.set(new KeyspacePair(dualKeyspaceSetup, primaryKS, secondaryKS));
    executionStrategy = execStrategy;
  }
 
  private Keyspace getPrimaryKS() {
    return ksPair.get().getPrimaryKS();
  }
 
  public DualKeyspaceMetadata getDualKeyspaceMetadata() {
      return ksPair.get().getDualKSMetadata();
  }

    @Override
  public AstyanaxConfiguration getConfig() {
    return getPrimaryKS().getConfig();
  }

  @Override
  public String getKeyspaceName() {
    return getPrimaryKS().getKeyspaceName();
  }

  @Override
  public Partitioner getPartitioner() throws ConnectionException {
    return getPrimaryKS().getPartitioner();
  }

  @Override
  public String describePartitioner() throws ConnectionException {
    return getPrimaryKS().describePartitioner();
  }

  @Override
  public List<TokenRange> describeRing() throws ConnectionException {
    return getPrimaryKS().describeRing();
  }

  @Override
  public List<TokenRange> describeRing(String dc) throws ConnectionException {
    return getPrimaryKS().describeRing(dc);
  }

  @Override
  public List<TokenRange> describeRing(String dc, String rack) throws ConnectionException {
    return getPrimaryKS().describeRing(dc, rack);
  }

  @Override
  public List<TokenRange> describeRing(boolean cached) throws ConnectionException {
    return getPrimaryKS().describeRing(cached);
  }

  @Override
  public KeyspaceDefinition describeKeyspace() throws ConnectionException {
    return getPrimaryKS().describeKeyspace();
  }

  @Override
  public Properties getKeyspaceProperties() throws ConnectionException {
    return getPrimaryKS().getKeyspaceProperties();
  }

  @Override
  public Properties getColumnFamilyProperties(String columnFamily) throws ConnectionException {
    return getPrimaryKS().getColumnFamilyProperties(columnFamily);
  }

  @Override
  public SerializerPackage getSerializerPackage(String cfName, boolean ignoreErrors) throws ConnectionException, UnknownComparatorException {
    return getPrimaryKS().getSerializerPackage(cfName, ignoreErrors);
  }

  @Override
  public MutationBatch prepareMutationBatch() {
      if (dualWritesEnabled.get()) {
          KeyspacePair pair = ksPair.get();
            return new DualWritesMutationBatch(
                                               pair.getDualKSMetadata(),
                                               pair.getPrimaryKS().prepareMutationBatch(),
                                               pair.getSecondaryKS().prepareMutationBatch(),
                                               executionStrategy);
      } else {
          return getPrimaryKS().prepareMutationBatch();
      }
  }

  @Override
  public <K, C> ColumnFamilyQuery<K, C> prepareQuery(ColumnFamily<K, C> cf) {
    return getPrimaryKS().prepareQuery(cf);
  }

  @Override
  public <K, C> ColumnMutation prepareColumnMutation(ColumnFamily<K, C> columnFamily, K rowKey, C column) {
      KeyspacePair pair = ksPair.get();
      if (dualWritesEnabled.get()) {
         
          WriteMetadata md = new WriteMetadata(pair.getDualKSMetadata(), columnFamily.getName(), rowKey.toString());
          return new DualWritesColumnMutation(md,
                                              pair.getPrimaryKS()  .prepareColumnMutation(columnFamily, rowKey, column),
                                              pair.getSecondaryKS().prepareColumnMutation(columnFamily, rowKey, column),
                                              executionStrategy);
      } else {
          return pair.getPrimaryKS().prepareColumnMutation(columnFamily, rowKey, column);
      }
  }

  @Override
  public <K, C> OperationResult<Void> truncateColumnFamily(final ColumnFamily<K, C> columnFamily) throws OperationException, ConnectionException {
       
      return execDualKeyspaceOperation(new KeyspaceOperation<Void>() {
            @Override
            public OperationResult<Void> exec(Keyspace ks) throws ConnectionException {
                return ks.truncateColumnFamily(columnFamily);
            }
      });
  }

  @Override
  public OperationResult<Void> truncateColumnFamily(final String columnFamily) throws ConnectionException {
      return execDualKeyspaceOperation(new KeyspaceOperation<Void>() {
            @Override
            public OperationResult<Void> exec(Keyspace ks) throws ConnectionException {
                return ks.truncateColumnFamily(columnFamily);
            }
        });
  }

  @Override
  public OperationResult<Void> testOperation(final Operation<?, ?> operation) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<Void>() {
            @Override
            public OperationResult<Void> exec(Keyspace ks) throws ConnectionException {
                return ks.testOperation(operation);
            }
        });
  }

  @Override
  public OperationResult<Void> testOperation(final Operation<?, ?> operation, RetryPolicy retry) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<Void>() {
            @Override
            public OperationResult<Void> exec(Keyspace ks) throws ConnectionException {
                return ks.testOperation(operation);
            }
        });
  }

  @Override
  public <K, C> OperationResult<SchemaChangeResult> createColumnFamily(final ColumnFamily<K, C> columnFamily, final Map<String, Object> options) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createColumnFamily(columnFamily, options);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createColumnFamily(final Properties props) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createColumnFamily(props);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createColumnFamily(final Map<String, Object> options) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createColumnFamily(options);
            }
        });
  }

  @Override
  public <K, C> OperationResult<SchemaChangeResult> updateColumnFamily(final ColumnFamily<K, C> columnFamily, final Map<String, Object> options) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.updateColumnFamily(columnFamily, options);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> updateColumnFamily(final Properties props) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.updateColumnFamily(props);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> updateColumnFamily(final Map<String, Object> options) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.updateColumnFamily(options);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> dropColumnFamily(final String columnFamilyName) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.dropColumnFamily(columnFamilyName);
            }
        });
  }

  @Override
  public <K, C> OperationResult<SchemaChangeResult> dropColumnFamily(final ColumnFamily<K, C> columnFamily) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.dropColumnFamily(columnFamily);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createKeyspace(final Map<String, Object> options) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createKeyspace(options);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createKeyspaceIfNotExists(final Map<String, Object> options) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createKeyspaceIfNotExists(options);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createKeyspace(final Properties properties) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createKeyspace(properties);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createKeyspaceIfNotExists(final Properties properties) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createKeyspaceIfNotExists(properties);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createKeyspace(final Map<String, Object> options, final Map<ColumnFamily, Map<String, Object>> cfsthrows ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createKeyspace(options, cfs);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> createKeyspaceIfNotExists(final Map<String, Object> options, final Map<ColumnFamily, Map<String, Object>> cfs) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.createKeyspaceIfNotExists(options, cfs);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> updateKeyspace(final Map<String, Object> options) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.updateKeyspace(options);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> updateKeyspace(final Properties props) throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.updateKeyspace(props);
            }
        });
  }

  @Override
  public OperationResult<SchemaChangeResult> dropKeyspace() throws ConnectionException {
        return execDualKeyspaceOperation(new KeyspaceOperation<SchemaChangeResult>() {
            @Override
            public OperationResult<SchemaChangeResult> exec(Keyspace ks) throws ConnectionException {
                return ks.dropKeyspace();
            }
        });
  }

  @Override
  public Map<String, List<String>> describeSchemaVersions() throws ConnectionException {
    return getPrimaryKS().describeSchemaVersions();
  }

  @Override
  public CqlStatement prepareCqlStatement() {
      KeyspacePair pair = ksPair.get();
        CqlStatement primaryStmt = pair.getPrimaryKS().prepareCqlStatement();
        CqlStatement secondaryStmt = pair.getSecondaryKS().prepareCqlStatement();
        return new DualWritesCqlStatement(primaryStmt, secondaryStmt, executionStrategy, pair.getDualKSMetadata());
  }

  @Override
  public ConnectionPool<?> getConnectionPool() throws ConnectionException {
    return getPrimaryKS().getConnectionPool();
  }

  private class KeyspacePair {
   
        private final DualKeyspaceMetadata dualKeyspaceMetadata;
    private final Keyspace ksPrimary;
    private final Keyspace ksSecondary;
   
    private KeyspacePair(final DualKeyspaceMetadata dualKeyspaceSetup, final Keyspace pKS, final Keyspace sKS) {
            dualKeyspaceMetadata = dualKeyspaceSetup;
      ksPrimary = pKS;
      ksSecondary = sKS;
    }
   
        private Keyspace getPrimaryKS() {
            return ksPrimary;
        }

        private Keyspace getSecondaryKS() {
            return ksSecondary;
        }

        private DualKeyspaceMetadata getDualKSMetadata() {
            return dualKeyspaceMetadata;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((ksPrimary == null) ? 0 : ksPrimary.hashCode());
            result = prime * result + ((ksSecondary == null) ? 0 : ksSecondary.hashCode());
            result = prime * result + ((dualKeyspaceMetadata == null) ? 0 : dualKeyspaceMetadata.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
           
            if (this == obj) return true;
            if (obj == null) return false;
            if (getClass() != obj.getClass()) return false;
           
            KeyspacePair other = (KeyspacePair) obj;
            boolean equals = true;
           
            equals &= (ksPrimary == null) (other.ksPrimary == null) : (ksPrimary.equals(other.ksPrimary));
            equals &= (ksSecondary == null) (other.ksSecondary == null) : (ksSecondary.equals(other.ksSecondary));
            equals &= (dualKeyspaceMetadata == null) (other.dualKeyspaceMetadata == null) : (dualKeyspaceMetadata.equals(other.dualKeyspaceMetadata));
           
            return equals;
        }
  }

    @Override
    public void dualWritesEnabled() {
        Logger.info("ENABLING dual writes for dual keyspace setup: " + ksPair.get().getDualKSMetadata());
        dualWritesEnabled.set(true);
    }

    @Override
    public void dualWritesDisabled() {
        Logger.info("DISABLING dual writes for dual keyspace setup: " + ksPair.get().getDualKSMetadata());
        dualWritesEnabled.set(false);
    }

    @Override
    public void flipPrimaryAndSecondary() {
       
        // Check that the expected state is actually reverse of what the destination state should be
        KeyspacePair currentPair = ksPair.get();

        DualKeyspaceMetadata currentKeyspaceSetup = currentPair.getDualKSMetadata();
       
        DualKeyspaceMetadata newDualKeyspaceSetup =
                new DualKeyspaceMetadata(currentKeyspaceSetup.getSecondaryCluster(), currentKeyspaceSetup.getSecondaryKeyspaceName(),
                                         currentKeyspaceSetup.getPrimaryCluster(), currentKeyspaceSetup.getPrimaryKeyspaceName());

        KeyspacePair newPair =
                new KeyspacePair(newDualKeyspaceSetup, currentPair.getSecondaryKS(), currentPair.getPrimaryKS());
        boolean success = ksPair.compareAndSet(currentPair, newPair);
           
        if (success) {
            Logger.info("Successfully flipped to new dual keyspace setup" + ksPair.get().getDualKSMetadata());
        } else {
            Logger.info("Could not flip keyspace pair: " + currentPair + " to new pair: " + newPair);
        }
    }
   
    private abstract class SimpleSyncExec<R> implements Execution<R> {
        @Override
        public ListenableFuture<OperationResult<R>> executeAsync() throws ConnectionException {
            throw new RuntimeException("executeAsync not implemented for SimpleSyncExec");
        }
    }
   
    private interface KeyspaceOperation<R> {
        OperationResult<R> exec(Keyspace ks) throws ConnectionException;
    }
   
    private <R> OperationResult<R> execDualKeyspaceOperation(final KeyspaceOperation<R> ksOperation) throws ConnectionException {
       
        final KeyspacePair pair = ksPair.get();
               
        final Execution<R> exec1 = new SimpleSyncExec<R>() {
            @Override
            public OperationResult<R> execute() throws ConnectionException {
                return ksOperation.exec(pair.getPrimaryKS());
            }
        };
        final Execution<R> exec2 = new SimpleSyncExec<R>() {
            @Override
            public OperationResult<R> execute() throws ConnectionException {
                return ksOperation.exec(pair.getSecondaryKS());
            }
        };
       
        WriteMetadata writeMd = new WriteMetadata(pair.getDualKSMetadata(), null, null);

        return executionStrategy.wrapExecutions(exec1, exec2, Collections.singletonList(writeMd)).execute();
    }

}
TOP

Related Classes of com.netflix.astyanax.contrib.dualwrites.DualWritesKeyspace$SimpleSyncExec

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.