All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.astyanax.recipes.uniqueness.RowUniquenessConstraint Maven / Gradle / Ivy

There is a newer version: 3.10.2
Show newest version
/*******************************************************************************
 * Copyright 2011 Netflix
 * 
 * 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.netflix.astyanax.recipes.uniqueness;

import java.nio.ByteBuffer;

import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.connectionpool.exceptions.NotFoundException;
import com.netflix.astyanax.model.Column;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.ColumnList;
import com.netflix.astyanax.model.ConsistencyLevel;
import com.netflix.astyanax.serializers.StringSerializer;

/**
 * Test uniqueness for a single row.  This implementation allows for any 
 * column type.  If the column family uses UTF8Type for the comparator
 * then it is preferable to use ColumnPrefixUniquenessConstraint.
 * 
 * @author elandau
 * 
 * @param 
 * @param 
 */
public class RowUniquenessConstraint implements UniquenessConstraint {
    private final ColumnFamily columnFamily;
    private final Keyspace   keyspace;
    private final C          uniqueColumn;
    private final K          key;
    private ConsistencyLevel consistencyLevel = ConsistencyLevel.CL_LOCAL_QUORUM;
    private ByteBuffer       data             = null;
    private Integer          ttl              = null;

    public RowUniquenessConstraint(Keyspace keyspace, ColumnFamily columnFamily, K key,
            Supplier uniqueColumnSupplier) {
        this.keyspace     = keyspace;
        this.columnFamily = columnFamily;
        this.uniqueColumn = uniqueColumnSupplier.get();
        this.key          = key;
    }

    public RowUniquenessConstraint withTtl(Integer ttl) {
        this.ttl = ttl;
        return this;
    }

    public RowUniquenessConstraint withConsistencyLevel(ConsistencyLevel consistencyLevel) {
        this.consistencyLevel = consistencyLevel;
        return this;
    }
    
    /**
     * Specify the data value to add to the column.  
     * @param data
     * @return
     */
    public RowUniquenessConstraint withData(ByteBuffer data) {
        this.data = data;
        return this;
    }
    
    public RowUniquenessConstraint withData(String data) {
        this.data = StringSerializer.get().fromString(data);
        return this;
    }
    
    @Override
    public void acquire() throws NotUniqueException, Exception {
        acquireAndApplyMutation(null);
    }
    
    /**
     * @deprecated  Use acquireAndExecuteMutation instead to avoid timestamp issues
     */
    @Override
    @Deprecated
    public void acquireAndMutate(final MutationBatch mutation) throws NotUniqueException, Exception {
        acquireAndApplyMutation(new Function() {
            @Override
            public Boolean apply(MutationBatch input) {
                if (mutation != null)
                    input.mergeShallow(mutation);
                return true;
            }
        });
    }
    
    @Override
    public void acquireAndApplyMutation(Function callback) throws NotUniqueException, Exception {
        try {
            // Phase 1: Write a unique column
            MutationBatch m = keyspace.prepareMutationBatch().setConsistencyLevel(consistencyLevel);
            if (data == null) {
                m.withRow(columnFamily, key).putEmptyColumn(uniqueColumn, ttl);
            }
            else {
                m.withRow(columnFamily, key).putColumn(uniqueColumn, data, ttl);
            }
            m.execute();

            // Phase 2: Read back all columns. There should be only 1
            ColumnList result = keyspace.prepareQuery(columnFamily).setConsistencyLevel(consistencyLevel)
                    .getKey(key).execute().getResult();

            if (result.size() != 1) {
                throw new NotUniqueException(key.toString());
            }

            // Phase 3: Persist the uniqueness with 
            m = keyspace.prepareMutationBatch().setConsistencyLevel(consistencyLevel);
            if (callback != null)
                callback.apply(m);
            
            if (data == null) {
                m.withRow(columnFamily, key).putEmptyColumn(uniqueColumn, null);
            }
            else {
                m.withRow(columnFamily, key).putColumn(uniqueColumn, data, null);
            }
            m.execute();
        }
        catch (Exception e) {
            release();
            throw e;
        }    }

    @Override
    public void release() throws Exception {
        MutationBatch m = keyspace.prepareMutationBatch().setConsistencyLevel(consistencyLevel);
        m.withRow(columnFamily, key).deleteColumn(uniqueColumn);
        m.execute();
    }
    
    /**
     * Read the data stored with the unique row.  This data is normally a 'foreign' key to
     * another column family.
     * @return
     * @throws Exception
     */
    public ByteBuffer readData() throws Exception {
        ColumnList result = keyspace
                .prepareQuery(columnFamily)
                    .setConsistencyLevel(consistencyLevel)
                    .getKey(key)
                .execute()
                    .getResult();
        
        boolean hasColumn = false;
        ByteBuffer data = null;
        for (Column column : result) {
            if (column.getTtl() == 0) {
                if (hasColumn) {
                    throw new IllegalStateException("Row has multiple uniquneness locks");
                }
                hasColumn = true;
                data = column.getByteBufferValue();
            }
        }
        
        if (!hasColumn) {
            throw new NotFoundException(this.key.toString() + " has no uniquness lock");
        }
        return data;
    }
    
    public String readDataAsString() throws Exception {
        return StringSerializer.get().fromByteBuffer(readData());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy