com.couchbase.client.java.bucket.BucketFlusher Maven / Gradle / Ivy
/**
* Copyright (C) 2014 Couchbase, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING
* IN THE SOFTWARE.
*/
package com.couchbase.client.java.bucket;
import com.couchbase.client.core.ClusterFacade;
import com.couchbase.client.core.CouchbaseException;
import com.couchbase.client.core.annotations.InterfaceAudience;
import com.couchbase.client.core.annotations.InterfaceStability;
import com.couchbase.client.core.message.ResponseStatus;
import com.couchbase.client.core.message.config.FlushRequest;
import com.couchbase.client.core.message.config.FlushResponse;
import com.couchbase.client.core.message.kv.GetRequest;
import com.couchbase.client.core.message.kv.GetResponse;
import com.couchbase.client.core.message.kv.UpsertRequest;
import com.couchbase.client.core.message.kv.UpsertResponse;
import com.couchbase.client.deps.io.netty.buffer.Unpooled;
import com.couchbase.client.deps.io.netty.util.CharsetUtil;
import com.couchbase.client.java.error.FlushDisabledException;
import rx.Observable;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.functions.Func2;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Helper class to flush a bucket properly and wait for it to be completed.
*
* @author Michael Nitschinger
* @since 2.1.1
*/
@InterfaceStability.Uncommitted
@InterfaceAudience.Private
public class BucketFlusher {
/**
* The number of marker documents to create, defaults to the number of partitions.
*
* This is important to make sure all the individual vbuckets are actually flushed.
*/
static final int FLUSH_MARKER_SIZE = 1024;
private static final List FLUSH_MARKERS = new ArrayList();
static {
for (int i = 0; i < FLUSH_MARKER_SIZE; i++) {
FLUSH_MARKERS.add("__flush_marker_" + i);
}
}
private BucketFlusher() {
}
/**
* Flush the bucket and make sure flush is complete before completing the observable.
*
* @param core the core reference.
* @param bucket the bucket to flush.
* @param password the password of the bucket.
* @return an observable which is completed once the flush process is done.
*/
public static Observable flush(final ClusterFacade core, final String bucket, final String password) {
return createMarkerDocuments(core, bucket)
.flatMap(new Func1, Observable>() {
@Override
public Observable call(List strings) {
return initiateFlush(core, bucket, password);
}
})
.flatMap(new Func1>() {
@Override
public Observable call(Boolean isDone) {
return isDone ? Observable.just(true) : pollMarkerDocuments(core, bucket);
}
});
}
/**
* Helper method to create marker documents for each partition.
*
* @param core the core reference.
* @param bucket the name of the bucket.
* @return a list of created flush marker IDs once they are completely upserted.
*/
private static Observable> createMarkerDocuments(final ClusterFacade core, final String bucket) {
return Observable
.from(FLUSH_MARKERS)
.flatMap(new Func1>() {
@Override
public Observable call(String id) {
return core.send(new UpsertRequest(id, Unpooled.copiedBuffer(id, CharsetUtil.UTF_8), bucket));
}
})
.doOnNext(new Action1() {
@Override
public void call(UpsertResponse response) {
if (response.content() != null && response.content().refCnt() > 0) {
response.content().release();
}
}
})
.last()
.map(new Func1>() {
@Override
public List call(UpsertResponse response) {
return FLUSH_MARKERS;
}
});
}
/**
* Initiates a flush request against the server.
*
* The result indicates if polling needs to be done or the flush is already complete. It can also fail in case
* flush is disabled or something else went wrong in the server response.
*
* @param core the core reference.
* @param bucket the bucket to flush.
* @param password the password of the bucket.
* @return an observable indicating if done (true) or polling needs to happen (false).
*/
private static Observable initiateFlush(final ClusterFacade core, final String bucket, final String password) {
return core
.send(new FlushRequest(bucket, password))
.map(new Func1() {
@Override
public Boolean call(FlushResponse flushResponse) {
if (flushResponse.status() == ResponseStatus.FAILURE) {
if (flushResponse.content().contains("disabled")) {
throw new FlushDisabledException("Flush is disabled for this bucket.");
} else {
throw new CouchbaseException("Flush failed because of: " + flushResponse.content());
}
}
return flushResponse.isDone();
}
});
}
/**
* Helper method to poll the list of marker documents until all of them are gone.
*
* @param core the core reference.
* @param bucket the name of the bucket.
* @return an observable completing when all marker documents are gone.
*/
private static Observable pollMarkerDocuments(final ClusterFacade core, final String bucket) {
return Observable
.from(FLUSH_MARKERS)
.flatMap(new Func1>() {
@Override
public Observable call(String id) {
return core.send(new GetRequest(id, bucket));
}
})
.reduce(0, new Func2() {
@Override
public Integer call(Integer foundDocs, GetResponse response) {
if (response.content() != null && response.content().refCnt() > 0) {
response.content().release();
}
if (response.status() == ResponseStatus.SUCCESS) {
foundDocs++;
}
return foundDocs;
}
})
.filter(new Func1() {
@Override
public Boolean call(Integer foundDocs) {
return foundDocs == 0;
}
})
.repeatWhen(new Func1, Observable>>() {
@Override
public Observable> call(Observable extends Void> observable) {
return observable.flatMap(new Func1>() {
@Override
public Observable> call(Void aVoid) {
return Observable.timer(500, TimeUnit.MILLISECONDS);
}
});
}
})
.take(1)
.map(new Func1() {
@Override
public Boolean call(Integer integer) {
return true;
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy