View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.procedure2;
20  
21  import com.google.common.base.Preconditions;
22  import com.google.protobuf.ByteString;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.lang.reflect.Constructor;
27  import java.lang.reflect.Modifier;
28  import java.util.Arrays;
29  import java.util.List;
30  import java.util.Map;
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.ProcedureInfo;
33  import org.apache.hadoop.hbase.classification.InterfaceAudience;
34  import org.apache.hadoop.hbase.classification.InterfaceStability;
35  import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
36  import org.apache.hadoop.hbase.procedure2.util.StringUtils;
37  import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos;
38  import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos.ProcedureState;
39  import org.apache.hadoop.hbase.util.ByteStringer;
40  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
41  import org.apache.hadoop.hbase.util.NonceKey;
42  
43  /**
44   * Base Procedure class responsible to handle the Procedure Metadata
45   * e.g. state, startTime, lastUpdate, stack-indexes, ...
46   *
47   * execute() is called each time the procedure is executed.
48   * it may be called multiple times in case of failure and restart, so the
49   * code must be idempotent.
50   * the return is a set of sub-procedures or null in case the procedure doesn't
51   * have sub-procedures. Once the sub-procedures are successfully completed
52   * the execute() method is called again, you should think at it as a stack:
53   *  -> step 1
54   *  ---> step 2
55   *  -> step 1
56   *
57   * rollback() is called when the procedure or one of the sub-procedures is failed.
58   * the rollback step is supposed to cleanup the resources created during the
59   * execute() step. in case of failure and restart rollback() may be called
60   * multiple times, so the code must be idempotent.
61   */
62  @InterfaceAudience.Private
63  @InterfaceStability.Evolving
64  public abstract class Procedure<TEnvironment> implements Comparable<Procedure> {
65    // unchanged after initialization
66    private String owner = null;
67    private Long parentProcId = null;
68    private Long procId = null;
69    private long startTime;
70  
71    // runtime state, updated every operation
72    private ProcedureState state = ProcedureState.INITIALIZING;
73    private Integer timeout = null;
74    private int[] stackIndexes = null;
75    private int childrenLatch = 0;
76    private long lastUpdate;
77  
78    private RemoteProcedureException exception = null;
79    private byte[] result = null;
80  
81    private NonceKey nonceKey = null;
82  
83    /**
84     * The main code of the procedure. It must be idempotent since execute()
85     * may be called multiple time in case of machine failure in the middle
86     * of the execution.
87     * @param env the environment passed to the ProcedureExecutor
88     * @return a set of sub-procedures or null if there is nothing else to execute.
89     * @throws ProcedureYieldException the procedure will be added back to the queue and retried later
90     * @throws InterruptedException the procedure will be added back to the queue and retried later
91     */
92    protected abstract Procedure[] execute(TEnvironment env)
93      throws ProcedureYieldException, InterruptedException;
94  
95    /**
96     * The code to undo what done by the execute() code.
97     * It is called when the procedure or one of the sub-procedure failed or an
98     * abort was requested. It should cleanup all the resources created by
99     * the execute() call. The implementation must be idempotent since rollback()
100    * may be called multiple time in case of machine failure in the middle
101    * of the execution.
102    * @param env the environment passed to the ProcedureExecutor
103    * @throws IOException temporary failure, the rollback will retry later
104    * @throws InterruptedException the procedure will be added back to the queue and retried later
105    */
106   protected abstract void rollback(TEnvironment env)
107     throws IOException, InterruptedException;
108 
109   /**
110    * The abort() call is asynchronous and each procedure must decide how to deal
111    * with that, if they want to be abortable. The simplest implementation
112    * is to have an AtomicBoolean set in the abort() method and then the execute()
113    * will check if the abort flag is set or not.
114    * abort() may be called multiple times from the client, so the implementation
115    * must be idempotent.
116    *
117    * NOTE: abort() is not like Thread.interrupt() it is just a notification
118    * that allows the procedure implementor where to abort to avoid leak and
119    * have a better control on what was executed and what not.
120    */
121   protected abstract boolean abort(TEnvironment env);
122 
123   /**
124    * The user-level code of the procedure may have some state to
125    * persist (e.g. input arguments) to be able to resume on failure.
126    * @param stream the stream that will contain the user serialized data
127    */
128   protected abstract void serializeStateData(final OutputStream stream)
129     throws IOException;
130 
131   /**
132    * Called on store load to allow the user to decode the previously serialized
133    * state.
134    * @param stream the stream that contains the user serialized data
135    */
136   protected abstract void deserializeStateData(final InputStream stream)
137     throws IOException;
138 
139   /**
140    * The user should override this method, and try to take a lock if necessary.
141    * A lock can be anything, and it is up to the implementor.
142    * Example: in our Master we can execute request in parallel for different tables
143    *          create t1 and create t2 can be executed at the same time.
144    *          anything else on t1/t2 is queued waiting that specific table create to happen.
145    *
146    * @return true if the lock was acquired and false otherwise
147    */
148   protected boolean acquireLock(final TEnvironment env) {
149     return true;
150   }
151 
152   /**
153    * The user should override this method, and release lock if necessary.
154    */
155   protected void releaseLock(final TEnvironment env) {
156     // no-op
157   }
158 
159   /**
160    * Called when the procedure is loaded for replay.
161    * The procedure implementor may use this method to perform some quick
162    * operation before replay.
163    * e.g. failing the procedure if the state on replay may be unknown.
164    */
165   protected void beforeReplay(final TEnvironment env) {
166     // no-op
167   }
168 
169   /**
170    * Called when the procedure is marked as completed (success or rollback).
171    * The procedure implementor may use this method to cleanup in-memory states.
172    * This operation will not be retried on failure.
173    */
174   protected void completionCleanup(final TEnvironment env) {
175     // no-op
176   }
177 
178   /**
179    * By default, the executor will try ro run procedures start to finish.
180    * Return true to make the executor yield between each execution step to
181    * give other procedures time to run their steps.
182    * @param env the environment passed to the ProcedureExecutor
183    * @return Return true if the executor should yield on completion of an execution step.
184    *         Defaults to return false.
185    */
186   protected boolean isYieldAfterExecutionStep(final TEnvironment env) {
187     return false;
188   }
189 
190   /**
191    * By default, the executor will keep the procedure result around util
192    * the eviction TTL is expired. The client can cut down the waiting time
193    * by requesting that the result is removed from the executor.
194    * In case of system started procedure, we can force the executor to auto-ack.
195    * @param env the environment passed to the ProcedureExecutor
196    * @return true if the executor should wait the client ack for the result.
197    *         Defaults to return true.
198    */
199   protected boolean shouldWaitClientAck(final TEnvironment env) {
200     return true;
201   }
202 
203   @Override
204   public String toString() {
205     // Return the simple String presentation of the procedure.
206     return toStringSimpleSB().toString();
207   }
208 
209   /**
210    * Build the StringBuilder for the simple form of
211    * procedure string.
212    * @return the StringBuilder
213    */
214   protected StringBuilder toStringSimpleSB() {
215     StringBuilder sb = new StringBuilder();
216     toStringClassDetails(sb);
217 
218     if (procId != null) {
219       sb.append(" id=");
220       sb.append(getProcId());
221     }
222 
223     if (hasParent()) {
224       sb.append(" parent=");
225       sb.append(getParentProcId());
226     }
227 
228     if (hasOwner()) {
229       sb.append(" owner=");
230       sb.append(getOwner());
231     }
232 
233     sb.append(" state=");
234     toStringState(sb);
235 
236     return sb;
237   }
238 
239   /**
240    * Extend the toString() information with more procedure
241    * details
242    */
243   public String toStringDetails() {
244     StringBuilder sb = toStringSimpleSB();
245 
246     sb.append(" startTime=");
247     sb.append(getStartTime());
248 
249     sb.append(" lastUpdate=");
250     sb.append(getLastUpdate());
251 
252     int[] stackIndices = getStackIndexes();
253     if (stackIndices != null) {
254       sb.append("\n");
255       sb.append("stackIndexes=");
256       sb.append(Arrays.toString(stackIndices));
257     }
258 
259     return sb.toString();
260   }
261 
262   protected String toStringClass() {
263     StringBuilder sb = new StringBuilder();
264     toStringClassDetails(sb);
265 
266     return sb.toString();
267   }
268 
269   /**
270    * Called from {@link #toString()} when interpolating {@link Procedure} state
271    * @param builder Append current {@link ProcedureState}
272    */
273   protected void toStringState(StringBuilder builder) {
274     builder.append(getState());
275   }
276 
277   /**
278    * Extend the toString() information with the procedure details
279    * e.g. className and parameters
280    * @param builder the string builder to use to append the proc specific information
281    */
282   protected void toStringClassDetails(StringBuilder builder) {
283     builder.append(getClass().getName());
284   }
285 
286   /**
287    * @return the serialized result if any, otherwise null
288    */
289   public byte[] getResult() {
290     return result;
291   }
292 
293   /**
294    * The procedure may leave a "result" on completion.
295    * @param result the serialized result that will be passed to the client
296    */
297   protected void setResult(final byte[] result) {
298     this.result = result;
299   }
300 
301   public long getProcId() {
302     return procId;
303   }
304 
305   public boolean hasParent() {
306     return parentProcId != null;
307   }
308 
309   public boolean hasException() {
310     return exception != null;
311   }
312 
313   public boolean hasTimeout() {
314     return timeout != null;
315   }
316 
317   public long getParentProcId() {
318     return parentProcId;
319   }
320 
321   public NonceKey getNonceKey() {
322     return nonceKey;
323   }
324 
325   /**
326    * @return true if the procedure is in a RUNNABLE state.
327    */
328   protected synchronized boolean isRunnable() {
329     return state == ProcedureState.RUNNABLE;
330   }
331 
332   /**
333    * @return true if the procedure has failed.
334    *         true may mean failed but not yet rolledback or failed and rolledback.
335    */
336   public synchronized boolean isFailed() {
337     return exception != null || state == ProcedureState.ROLLEDBACK;
338   }
339 
340   /**
341    * @return true if the procedure is finished successfully.
342    */
343   public synchronized boolean isSuccess() {
344     return state == ProcedureState.FINISHED && exception == null;
345   }
346 
347   /**
348    * @return true if the procedure is finished. The Procedure may be completed
349    *         successfuly or failed and rolledback.
350    */
351   public synchronized boolean isFinished() {
352     switch (state) {
353       case ROLLEDBACK:
354         return true;
355       case FINISHED:
356         return exception == null;
357       default:
358         break;
359     }
360     return false;
361   }
362 
363   /**
364    * @return true if the procedure is waiting for a child to finish or for an external event.
365    */
366   public synchronized boolean isWaiting() {
367     switch (state) {
368       case WAITING:
369       case WAITING_TIMEOUT:
370         return true;
371       default:
372         break;
373     }
374     return false;
375   }
376 
377   public synchronized RemoteProcedureException getException() {
378     return exception;
379   }
380 
381   public long getStartTime() {
382     return startTime;
383   }
384 
385   public synchronized long getLastUpdate() {
386     return lastUpdate;
387   }
388 
389   public synchronized long elapsedTime() {
390     return lastUpdate - startTime;
391   }
392 
393   /**
394    * @param timeout timeout in msec
395    */
396   protected void setTimeout(final int timeout) {
397     this.timeout = timeout;
398   }
399 
400   /**
401    * @return the timeout in msec
402    */
403   public int getTimeout() {
404     return timeout;
405   }
406 
407   /**
408    * @return the remaining time before the timeout
409    */
410   public long getTimeRemaining() {
411     return Math.max(0, timeout - (EnvironmentEdgeManager.currentTime() - startTime));
412   }
413 
414   @InterfaceAudience.Private
415   public void setOwner(final String owner) {
416     this.owner = StringUtils.isEmpty(owner) ? null : owner;
417   }
418 
419   public String getOwner() {
420     return owner;
421   }
422 
423   public boolean hasOwner() {
424     return owner != null;
425   }
426 
427   @InterfaceAudience.Private
428   protected synchronized void setState(final ProcedureState state) {
429     this.state = state;
430     updateTimestamp();
431   }
432 
433   @InterfaceAudience.Private
434   protected synchronized ProcedureState getState() {
435     return state;
436   }
437 
438   protected void setFailure(final String source, final Throwable cause) {
439     setFailure(new RemoteProcedureException(source, cause));
440   }
441 
442   protected synchronized void setFailure(final RemoteProcedureException exception) {
443     this.exception = exception;
444     if (!isFinished()) {
445       setState(ProcedureState.FINISHED);
446     }
447   }
448 
449   protected void setAbortFailure(final String source, final String msg) {
450     setFailure(source, new ProcedureAbortedException(msg));
451   }
452 
453   @InterfaceAudience.Private
454   protected synchronized boolean setTimeoutFailure() {
455     if (state == ProcedureState.WAITING_TIMEOUT) {
456       long timeDiff = EnvironmentEdgeManager.currentTime() - lastUpdate;
457       setFailure("ProcedureExecutor", new TimeoutIOException(
458         "Operation timed out after " + StringUtils.humanTimeDiff(timeDiff)));
459       return true;
460     }
461     return false;
462   }
463 
464   /**
465    * Called by the ProcedureExecutor to assign the ID to the newly created procedure.
466    */
467   @InterfaceAudience.Private
468   protected void setProcId(final long procId) {
469     this.procId = procId;
470     this.startTime = EnvironmentEdgeManager.currentTime();
471     setState(ProcedureState.RUNNABLE);
472   }
473 
474   /**
475    * Called by the ProcedureExecutor to assign the parent to the newly created procedure.
476    */
477   @InterfaceAudience.Private
478   protected void setParentProcId(final long parentProcId) {
479     this.parentProcId = parentProcId;
480   }
481 
482   /**
483    * Called by the ProcedureExecutor to set the value to the newly created procedure.
484    */
485   @InterfaceAudience.Private
486   protected void setNonceKey(final NonceKey nonceKey) {
487     this.nonceKey = nonceKey;
488   }
489 
490   /**
491    * Internal method called by the ProcedureExecutor that starts the
492    * user-level code execute().
493    */
494   @InterfaceAudience.Private
495   protected Procedure[] doExecute(final TEnvironment env)
496       throws ProcedureYieldException, InterruptedException {
497     try {
498       updateTimestamp();
499       return execute(env);
500     } finally {
501       updateTimestamp();
502     }
503   }
504 
505   /**
506    * Internal method called by the ProcedureExecutor that starts the
507    * user-level code rollback().
508    */
509   @InterfaceAudience.Private
510   protected void doRollback(final TEnvironment env)
511       throws IOException, InterruptedException {
512     try {
513       updateTimestamp();
514       rollback(env);
515     } finally {
516       updateTimestamp();
517     }
518   }
519 
520   /**
521    * Called on store load to initialize the Procedure internals after
522    * the creation/deserialization.
523    */
524   @InterfaceAudience.Private
525   protected void setStartTime(final long startTime) {
526     this.startTime = startTime;
527   }
528 
529   /**
530    * Called on store load to initialize the Procedure internals after
531    * the creation/deserialization.
532    */
533   private synchronized void setLastUpdate(final long lastUpdate) {
534     this.lastUpdate = lastUpdate;
535   }
536 
537   protected synchronized void updateTimestamp() {
538     this.lastUpdate = EnvironmentEdgeManager.currentTime();
539   }
540 
541   /**
542    * Called by the ProcedureExecutor on procedure-load to restore the latch state
543    */
544   @InterfaceAudience.Private
545   protected synchronized void setChildrenLatch(final int numChildren) {
546     this.childrenLatch = numChildren;
547   }
548 
549   /**
550    * Called by the ProcedureExecutor on procedure-load to restore the latch state
551    */
552   @InterfaceAudience.Private
553   protected synchronized void incChildrenLatch() {
554     // TODO: can this be inferred from the stack? I think so...
555     this.childrenLatch++;
556   }
557 
558   /**
559    * Called by the ProcedureExecutor to notify that one of the sub-procedures
560    * has completed.
561    */
562   @InterfaceAudience.Private
563   protected synchronized boolean childrenCountDown() {
564     assert childrenLatch > 0;
565     return --childrenLatch == 0;
566   }
567 
568   @InterfaceAudience.Private
569   protected synchronized boolean hasChildren() {
570     return childrenLatch > 0;
571   }
572 
573   /**
574    * Called by the RootProcedureState on procedure execution.
575    * Each procedure store its stack-index positions.
576    */
577   @InterfaceAudience.Private
578   protected synchronized void addStackIndex(final int index) {
579     if (stackIndexes == null) {
580       stackIndexes = new int[] { index };
581     } else {
582       int count = stackIndexes.length;
583       stackIndexes = Arrays.copyOf(stackIndexes, count + 1);
584       stackIndexes[count] = index;
585     }
586   }
587 
588   @InterfaceAudience.Private
589   protected synchronized boolean removeStackIndex() {
590     if (stackIndexes != null && stackIndexes.length > 1) {
591       stackIndexes = Arrays.copyOf(stackIndexes, stackIndexes.length - 1);
592       return false;
593     } else {
594       stackIndexes = null;
595       return true;
596     }
597   }
598 
599   /**
600    * Called on store load to initialize the Procedure internals after
601    * the creation/deserialization.
602    */
603   @InterfaceAudience.Private
604   protected synchronized void setStackIndexes(final List<Integer> stackIndexes) {
605     this.stackIndexes = new int[stackIndexes.size()];
606     for (int i = 0; i < this.stackIndexes.length; ++i) {
607       this.stackIndexes[i] = stackIndexes.get(i);
608     }
609   }
610 
611   @InterfaceAudience.Private
612   protected synchronized boolean wasExecuted() {
613     return stackIndexes != null;
614   }
615 
616   @InterfaceAudience.Private
617   protected synchronized int[] getStackIndexes() {
618     return stackIndexes;
619   }
620 
621   @Override
622   public int compareTo(final Procedure other) {
623     long diff = getProcId() - other.getProcId();
624     return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
625   }
626 
627   /**
628    * Get an hashcode for the specified Procedure ID
629    * @return the hashcode for the specified procId
630    */
631   public static long getProcIdHashCode(final long procId) {
632     long h = procId;
633     h ^= h >> 16;
634     h *= 0x85ebca6b;
635     h ^= h >> 13;
636     h *= 0xc2b2ae35;
637     h ^= h >> 16;
638     return h;
639   }
640 
641   /*
642    * Helper to lookup the root Procedure ID given a specified procedure.
643    */
644   @InterfaceAudience.Private
645   protected static Long getRootProcedureId(final Map<Long, Procedure> procedures, Procedure proc) {
646     while (proc.hasParent()) {
647       proc = procedures.get(proc.getParentProcId());
648       if (proc == null) return null;
649     }
650     return proc.getProcId();
651   }
652 
653   protected static Procedure newInstance(final String className) throws IOException {
654     try {
655       Class<?> clazz = Class.forName(className);
656       if (!Modifier.isPublic(clazz.getModifiers())) {
657         throw new Exception("the " + clazz + " class is not public");
658       }
659 
660       Constructor<?> ctor = clazz.getConstructor();
661       assert ctor != null : "no constructor found";
662       if (!Modifier.isPublic(ctor.getModifiers())) {
663         throw new Exception("the " + clazz + " constructor is not public");
664       }
665       return (Procedure)ctor.newInstance();
666     } catch (Exception e) {
667       throw new IOException("The procedure class " + className +
668           " must be accessible and have an empty constructor", e);
669     }
670   }
671 
672   protected static void validateClass(final Procedure proc) throws IOException {
673     try {
674       Class<?> clazz = proc.getClass();
675       if (!Modifier.isPublic(clazz.getModifiers())) {
676         throw new Exception("the " + clazz + " class is not public");
677       }
678 
679       Constructor<?> ctor = clazz.getConstructor();
680       assert ctor != null;
681       if (!Modifier.isPublic(ctor.getModifiers())) {
682         throw new Exception("the " + clazz + " constructor is not public");
683       }
684     } catch (Exception e) {
685       throw new IOException("The procedure class " + proc.getClass().getName() +
686           " must be accessible and have an empty constructor", e);
687     }
688   }
689 
690   /**
691    * Helper to create the ProcedureInfo from Procedure.
692    */
693   @InterfaceAudience.Private
694   public static ProcedureInfo createProcedureInfo(final Procedure proc, final NonceKey nonceKey) {
695     RemoteProcedureException exception = proc.hasException() ? proc.getException() : null;
696     return new ProcedureInfo(
697       proc.getProcId(),
698       proc.toStringClass(),
699       proc.getOwner(),
700       proc.getState(),
701       proc.hasParent() ? proc.getParentProcId() : -1,
702       nonceKey,
703       exception != null ?
704           RemoteProcedureException.toProto(exception.getSource(), exception.getCause()) : null,
705       proc.getLastUpdate(),
706       proc.getStartTime(),
707       proc.getResult());
708   }
709 
710   /**
711    * Helper to convert the procedure to protobuf.
712    * Used by ProcedureStore implementations.
713    */
714   @InterfaceAudience.Private
715   public static ProcedureProtos.Procedure convert(final Procedure proc)
716       throws IOException {
717     Preconditions.checkArgument(proc != null);
718     validateClass(proc);
719 
720     ProcedureProtos.Procedure.Builder builder = ProcedureProtos.Procedure.newBuilder()
721       .setClassName(proc.getClass().getName())
722       .setProcId(proc.getProcId())
723       .setState(proc.getState())
724       .setStartTime(proc.getStartTime())
725       .setLastUpdate(proc.getLastUpdate());
726 
727     if (proc.hasParent()) {
728       builder.setParentId(proc.getParentProcId());
729     }
730 
731     if (proc.hasTimeout()) {
732       builder.setTimeout(proc.getTimeout());
733     }
734 
735     if (proc.hasOwner()) {
736       builder.setOwner(proc.getOwner());
737     }
738 
739     int[] stackIds = proc.getStackIndexes();
740     if (stackIds != null) {
741       for (int i = 0; i < stackIds.length; ++i) {
742         builder.addStackId(stackIds[i]);
743       }
744     }
745 
746     if (proc.hasException()) {
747       RemoteProcedureException exception = proc.getException();
748       builder.setException(
749         RemoteProcedureException.toProto(exception.getSource(), exception.getCause()));
750     }
751 
752     byte[] result = proc.getResult();
753     if (result != null) {
754       builder.setResult(ByteStringer.wrap(result));
755     }
756 
757     ByteString.Output stateStream = ByteString.newOutput();
758     proc.serializeStateData(stateStream);
759     if (stateStream.size() > 0) {
760       builder.setStateData(stateStream.toByteString());
761     }
762 
763     if (proc.getNonceKey() != null) {
764       builder.setNonceGroup(proc.getNonceKey().getNonceGroup());
765       builder.setNonce(proc.getNonceKey().getNonce());
766     }
767 
768     return builder.build();
769   }
770 
771   /**
772    * Helper to convert the protobuf procedure.
773    * Used by ProcedureStore implementations.
774    *
775    * TODO: OPTIMIZATION: some of the field never change during the execution
776    *                     (e.g. className, procId, parentId, ...).
777    *                     We can split in 'data' and 'state', and the store
778    *                     may take advantage of it by storing the data only on insert().
779    */
780   @InterfaceAudience.Private
781   public static Procedure convert(final ProcedureProtos.Procedure proto)
782       throws IOException {
783     // Procedure from class name
784     Procedure proc = Procedure.newInstance(proto.getClassName());
785 
786     // set fields
787     proc.setProcId(proto.getProcId());
788     proc.setState(proto.getState());
789     proc.setStartTime(proto.getStartTime());
790     proc.setLastUpdate(proto.getLastUpdate());
791 
792     if (proto.hasParentId()) {
793       proc.setParentProcId(proto.getParentId());
794     }
795 
796     if (proto.hasOwner()) {
797       proc.setOwner(proto.getOwner());
798     }
799 
800     if (proto.hasTimeout()) {
801       proc.setTimeout(proto.getTimeout());
802     }
803 
804     if (proto.getStackIdCount() > 0) {
805       proc.setStackIndexes(proto.getStackIdList());
806     }
807 
808     if (proto.hasException()) {
809       assert proc.getState() == ProcedureState.FINISHED ||
810              proc.getState() == ProcedureState.ROLLEDBACK :
811              "The procedure must be failed (waiting to rollback) or rolledback";
812       proc.setFailure(RemoteProcedureException.fromProto(proto.getException()));
813     }
814 
815     if (proto.hasResult()) {
816       proc.setResult(proto.getResult().toByteArray());
817     }
818 
819     if (proto.getNonce() != HConstants.NO_NONCE) {
820       NonceKey nonceKey = new NonceKey(proto.getNonceGroup(), proto.getNonce());
821       proc.setNonceKey(nonceKey);
822     }
823 
824     // we want to call deserialize even when the stream is empty, mainly for testing.
825     proc.deserializeStateData(proto.getStateData().newInput());
826 
827     return proc;
828   }
829 }