/** * Licensed to the zk1931 under one or more contributor license * agreements. See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * The ASF licenses this file to you 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.github.zk1931.jzab; import com.google.protobuf.TextFormat; import java.io.File; import java.io.FileOutputStream; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import com.github.zk1931.jzab.proto.ZabMessage.Message; import com.github.zk1931.jzab.proto.ZabMessage.Message.MessageType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The processor which is responsible for taking the snapshot of application * when it's required. */ class SnapshotProcessor implements RequestProcessor, Callable<Void> { private final BlockingQueue<MessageTuple> requestQueue = new LinkedBlockingQueue<MessageTuple>(); private static final Logger LOG = LoggerFactory.getLogger(SnapshotProcessor.class); private final StateMachine stateMachine; private final PersistentState persistence; private final Transport transport; private final String serverId; Future<Void> ft; public SnapshotProcessor(StateMachine stateMachine, PersistentState persistence, String serverId, Transport transport) { this.stateMachine = stateMachine; this.persistence = persistence; this.serverId = serverId; this.transport = transport; ExecutorService es = Executors.newSingleThreadExecutor(DaemonThreadFactory.FACTORY); ft = es.submit(this); es.shutdown(); } @Override public void processRequest(MessageTuple request) { this.requestQueue.add(request); } @Override public void shutdown() throws InterruptedException, ExecutionException { this.requestQueue.add(MessageTuple.REQUEST_OF_DEATH); this.ft.get(); LOG.debug("SnapshotProcessor has been shut down."); } @Override public Void call() throws Exception { LOG.debug("SnapshotProcessor gets started."); try { while (true) { MessageTuple request = requestQueue.take(); if (request == MessageTuple.REQUEST_OF_DEATH) { break; } Message msg = request.getMessage(); if (msg.getType() == MessageType.SNAPSHOT) { Zxid zxid = MessageBuilder.fromProtoZxid(msg.getSnapshot().getLastZxid()); LOG.debug("Got SNAPSHOT, the zxid of last transaction which is " + "guaranteed in log is {}.", zxid); // Create a temporary file for snapshot. File temp = persistence.createTempFile("snapshot"); try (FileOutputStream fout = new FileOutputStream(temp)) { stateMachine.save(fout); fout.close(); // Mark it valid. File file = persistence.setSnapshotFile(temp, zxid); Message done = MessageBuilder.buildSnapshotDone(file.getPath()); // Sends it back to main thread. this.transport.send(this.serverId, done); } } else { if (LOG.isWarnEnabled()) { LOG.warn("Got unexpected message {}.", TextFormat.shortDebugString(msg)); } } } } catch (RuntimeException e) { LOG.error("Caught exception", e); throw e; } return null; } }