mirror of https://github.com/dapr/java-sdk.git
				
				
				
			IT for recovering reminder after sidecar restart. (#406)
This commit is contained in:
		
							parent
							
								
									adce91f743
								
							
						
					
					
						commit
						f871b1f51f
					
				|  | @ -0,0 +1,138 @@ | |||
| /* | ||||
|  * Copyright (c) Microsoft Corporation. | ||||
|  * Licensed under the MIT License. | ||||
|  */ | ||||
| 
 | ||||
| package io.dapr.it.actors; | ||||
| 
 | ||||
| import io.dapr.actors.ActorId; | ||||
| import io.dapr.actors.client.ActorProxy; | ||||
| import io.dapr.actors.client.ActorProxyBuilder; | ||||
| import io.dapr.it.AppRun; | ||||
| import io.dapr.it.BaseIT; | ||||
| import io.dapr.it.DaprRun; | ||||
| import io.dapr.it.actors.app.MyActorService; | ||||
| import org.apache.commons.lang3.tuple.ImmutablePair; | ||||
| import org.junit.After; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
| import java.util.stream.Collectors; | ||||
| 
 | ||||
| import static org.junit.Assert.assertTrue; | ||||
| 
 | ||||
| public class ActorReminderRecoveryIT extends BaseIT { | ||||
| 
 | ||||
|   private static Logger logger = LoggerFactory.getLogger(ActorReminderRecoveryIT.class); | ||||
| 
 | ||||
|   private ActorProxy proxy; | ||||
| 
 | ||||
|   private ImmutablePair<AppRun, DaprRun> runs; | ||||
| 
 | ||||
|   @Before | ||||
|   public void init() throws Exception { | ||||
|     runs = startSplitDaprAndApp( | ||||
|         ActorReminderRecoveryIT.class.getSimpleName(), | ||||
|         "Started MyActorService", | ||||
|         MyActorService.class, | ||||
|         true, | ||||
|         60000); | ||||
| 
 | ||||
|     Thread.sleep(3000); | ||||
| 
 | ||||
|     ActorId actorId = new ActorId(UUID.randomUUID().toString()); | ||||
|     String actorType="MyActorTest"; | ||||
|     logger.debug("Creating proxy builder"); | ||||
| 
 | ||||
|     ActorProxyBuilder<ActorProxy> proxyBuilder = deferClose(new ActorProxyBuilder(actorType, ActorProxy.class)); | ||||
|     logger.debug("Creating actorId"); | ||||
|     logger.debug("Building proxy"); | ||||
|     proxy = proxyBuilder.build(actorId); | ||||
|   } | ||||
| 
 | ||||
|   @After | ||||
|   public void tearDown() { | ||||
|     // call unregister | ||||
|     logger.debug("Calling actor method 'stopReminder' to unregister reminder"); | ||||
|     proxy.invokeActorMethod("stopReminder", "myReminder").block(); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Create an actor, register a reminder, validates its content, restarts the runtime and confirms reminder continues. | ||||
|    * @throws Exception This test is not expected to throw.  Thrown exceptions are bugs. | ||||
|    */ | ||||
|   @Test | ||||
|   public void reminderRecoveryTest() throws Exception { | ||||
|     logger.debug("Invoking actor method 'startReminder' which will register a reminder"); | ||||
|     proxy.invokeActorMethod("startReminder", "myReminder").block(); | ||||
| 
 | ||||
|     logger.debug("Pausing 7 seconds to allow reminder to fire"); | ||||
|     Thread.sleep(7000); | ||||
| 
 | ||||
|     ArrayList<MethodEntryTracker> logs = getAppMethodCallLogs(proxy); | ||||
|     validateReminderCalls(logs, 3); | ||||
| 
 | ||||
|     // Restarts runtime only. | ||||
|     runs.right.stop(); | ||||
|     runs.right.start(); | ||||
| 
 | ||||
|     logger.debug("Pausing 5 seconds to allow sidecar to be healthy"); | ||||
|     Thread.sleep(5000); | ||||
|     ArrayList<MethodEntryTracker> newLogs = getAppMethodCallLogs(proxy); | ||||
|     logger.debug("Pausing 10 seconds to allow reminder to fire a few times"); | ||||
|     Thread.sleep(10000); | ||||
|     ArrayList<MethodEntryTracker> newLogs2 = getAppMethodCallLogs(proxy); | ||||
|     logger.debug("Check if there has been additional calls"); | ||||
|     validateReminderCalls(newLogs2, countReminderCalls(newLogs) + 3); | ||||
|   } | ||||
| 
 | ||||
|   ArrayList<MethodEntryTracker> getAppMethodCallLogs(ActorProxy proxy) { | ||||
|     ArrayList<String> logs = proxy.invokeActorMethod("getCallLog", ArrayList.class).block(); | ||||
|     ArrayList<MethodEntryTracker> trackers = new ArrayList<MethodEntryTracker>(); | ||||
|     for(String t : logs) { | ||||
|       String[] toks = t.split("\\|"); | ||||
|       MethodEntryTracker m = new MethodEntryTracker( | ||||
|         toks[0].equals("Enter") ? true : false, | ||||
|         toks[1], | ||||
|         new Date(toks[2])); | ||||
|       trackers.add(m); | ||||
|     } | ||||
| 
 | ||||
|     return trackers; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Validate the reminder has been invoked at least x times. | ||||
|    * @param logs logs with info about method entries and exits returned from the app | ||||
|    * @return number of successful invocations of reminder | ||||
|    */ | ||||
|   private int countReminderCalls(ArrayList<MethodEntryTracker> logs) { | ||||
|     // Counts number of times reminder is invoked. | ||||
|     // Events for each actor method call include "enter" and "exit" calls, so they are divided by 2. | ||||
|     List<MethodEntryTracker> calls = | ||||
|         logs.stream().filter(x -> x.getMethodName().equals(("receiveReminder"))).collect(Collectors.toList()); | ||||
|     System.out.printf( | ||||
|         "Size of reminder count list is %d, which means it's been invoked half that many times.", calls.size()); | ||||
|     return calls.size() / 2; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Validate the reminder has been invoked at least x times. | ||||
|    * @param logs logs with info about method entries and exits returned from the app | ||||
|    * @param minimum minimum number of entries. | ||||
|    */ | ||||
|   void validateReminderCalls(ArrayList<MethodEntryTracker> logs, int minimum) { | ||||
|     // Validate the reminder has been invoked at least x times. We cannot validate precisely because of | ||||
|     // differences due issues like how loaded the machine may be. Based on its dueTime and period, and our sleep above, | ||||
|     // we validate below with some margin. | ||||
|     int callsCount = countReminderCalls(logs); | ||||
|     assertTrue(callsCount >= minimum); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -133,17 +133,18 @@ public class MyActorImpl extends AbstractActor implements MyActor, Remindable<St | |||
| 
 | ||||
|   @Override | ||||
|   public Mono<Void> receiveReminder(String reminderName, String state, Duration dueTime, Duration period) { | ||||
|     this.formatAndLog(true, "receiveReminder"); | ||||
|     Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT")); | ||||
|     String utcNowAsString = DATE_FORMAT.format(utcNow.getTime()); | ||||
|     return Mono.fromRunnable(() -> { | ||||
|       this.formatAndLog(true, "receiveReminder"); | ||||
|       Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT")); | ||||
|       String utcNowAsString = DATE_FORMAT.format(utcNow.getTime()); | ||||
| 
 | ||||
|     // Handles the request by printing message. | ||||
|     System.out.println(String.format( | ||||
|       "^^^^^^^^^^^^^^Server reminded actor %s of: %s for %s @ %s", | ||||
|       this.getId(), reminderName, state, utcNowAsString)); | ||||
|       // Handles the request by printing message. | ||||
|       System.out.println(String.format( | ||||
|           "^^^^^^^^^^^^^^Server reminded actor %s of: %s for %s @ %s", | ||||
|           this.getId(), reminderName, state, utcNowAsString)); | ||||
| 
 | ||||
|     this.formatAndLog(false, "receiveReminder"); | ||||
|     return Mono.empty(); | ||||
|       this.formatAndLog(false, "receiveReminder"); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   @Override | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue