com.couchbase.client.java.bucket.BucketFlusher Maven / Gradle / Ivy
/*
* Copyright (c) 2016 Couchbase, Inc.
*
* 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.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().isSuccess()) {
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