I agree with your analysis for long network disconnections. The mitigation for long two-way network disconnections is for the client to periodically try to connect to the Registrar regardless of the renewal process, with a predefined number of retries before discarding the Registrar.
The scenario below is different: 1. Short/sporadic network disconnections or short periods in which a specific client is unavailable (due to GC, or virtualization issues). 2. There is more than one registrar for high availability, but both could fail to send a notification to the listener and both would cancel the lease. 3. The client needs to get the notifications eventually. Where "eventually" should be configurable regardless of the server maxEventLease (assuming desiredExpiration=Lease.FOREVER). 30 seconds is a reasonable response time for a notification. maxEventLease/2 in our case is 15 minutes which is not a reasonable response time for a notification. Hence the suggestion for the client to renew the lease at least once every 30 seconds. It is true that if many clients, try to perform full recovery at the same time, it could cause the Registrar to be with high load (or worse, all Registrars). More so, if a nasty firewall rule is crafted to allow one-way connection from the listener to the Registrar but not the other way around. The rabbit hole goes deep :) Nevertheless, these scenarios are much less likely than the sporadic network failures described in 1 above. Itai -----Original Message----- From: Dan Creswell [mailto:[email protected]] Sent: Tuesday, July 10, 2012 2:35 PM To: [email protected] Subject: Re: Question about LeaseRenewalManager and renewDuration Hey, Thanks for including the background information, that makes it much easier to figure out how to answer the question. In this case, I'm not sure that increasing the frequency of lease renewals will help you. The same network problems that cause the Registrar to drop a listener can also foul up the lease renewal process such that it can take a long time for a renewal request to be processed. Basically, even though you make a call after about 20 seconds (the LRM takes some time off the overall lease renewal duration to account for network roundtrip) it could take substantial time to complete such that you aren't informed for much longer than the 30 seconds you are after. I guess the question then is: Why is it important to "know with certainty" that the lease has expired as soon as possible? Ultimately, we can't know precisely when the lease expires and when we figure out that expiry occurred we still have to: (1) Contact the registrar and recover state. (2) Register for events that update state recovered in (1). So our response is always the same, it can just be a little slow hence my question above: Why do you want to know "more quickly"? Best, Dan. On 10 July 2012 11:41, Itai Frenkel <[email protected]> wrote: > Hello, > > I would like to use the LeaseRenewalManager to renew the lease every > 30 seconds (desiredExpiration=Lease.FOREVER, renewDuration=30000). > I notice that the first renewal is delayed based on the server's > maxEventLease and the renewDuration kicks in only after subsequent renewals. > Below is the output of the reproduction code (notice how the lease is > first renewed after 2.5 minutes instead of 30 seconds). In this case > the mock maxEventLease is 5 minutes thus the lease is renewed after > 5/2=2.5 minutes. > The question is - what is the best way to add this functionality to > the LeaseRenewalManager. > > 10/07/2012 13:18:51 Main main > INFO: Starting... Press any key to abort > 10/07/2012 13:23:14 Main$MockEventLease doRenew > INFO: doRenew(duration=30000) returns 30000 > 10/07/2012 13:23:40 Main$MockEventLease doRenew > INFO: doRenew(duration=30000) returns 30000 > 10/07/2012 13:24:06 Main$MockEventLease doRenew > INFO: doRenew(duration=30000) returns 30000 > 10/07/2012 13:24:33 Main$MockEventLease doRenew > INFO: doRenew(duration=30000) returns 30000 > 10/07/2012 13:24:59 Main$MockEventLease doRenew > INFO: doRenew(duration=30000) returns 30000 > 10/07/2012 13:25:25 Main$MockEventLease doRenew > INFO: doRenew(duration=30000) returns 30000 > 10/07/2012 13:25:51 Main$MockEventLease doRenew > INFO: doRenew(duration=30000) returns 30000 > > Background Information: > The motivation for this is the way the Registrar handles event > notifications. > When the Registrar fails to send a notification to a listener due to a > temporary network glitch, it assumes the listener is no longer > available and cancels the event lease. > The listener may not detect the temporary network glitch and would > remain oblivious to the fact that it is no longer registered for events. > The fix is for the listener to send lease renew requests more often to > the Registrar (and to get an UnknkownLeaseException). > Thus the motivation for setting renewDuration to 30 seconds. > > Reproduction code for the LeaseRenewalManager renewDuration behavior > is pasted below. > > Any help would be appreciated, > Itai > > import java.io.IOException; > import java.rmi.RemoteException; > import java.util.HashMap; > import java.util.Map; > import java.util.concurrent.TimeUnit; > import java.util.logging.Logger; > > import net.jini.config.AbstractConfiguration; > import net.jini.config.ConfigurationException; > import net.jini.core.lease.Lease; > import net.jini.core.lease.UnknownLeaseException; > import net.jini.core.lookup.ServiceID; import net.jini.id.Uuid; import > net.jini.lease.DesiredExpirationListener; > import net.jini.lease.LeaseListener; > import net.jini.lease.LeaseRenewalEvent; import > net.jini.lease.LeaseRenewalManager; > > import com.sun.jini.reggie.EventLease; import > com.sun.jini.reggie.Registrar; import com.sun.jini.thread.TaskManager; > > public class Main { > > private static final Logger logger = Logger.getLogger("test-renew"); > private static final long MAX_EVENT_LEASE = > TimeUnit.MINUTES.toMillis(5); > private static final long RENEW_BATCH_TIME_WINDOW = > TimeUnit.MINUTES.toMillis(5); > private static final long ROUND_TRIP_TIME = > TimeUnit.SECONDS.toMillis(3); > private static final long RENEW_DURATION = > TimeUnit.SECONDS.toMillis(30); > > public static void main(String[] args) throws IOException, > ConfigurationException { > > LeaseRenewalManager lrm = new LeaseRenewalManager(new > ConfigurationMock()); > > Lease lease = > MockEventLease.getInstance(System.currentTimeMillis() + > MAX_EVENT_LEASE); > > long desiredDuration = Lease.FOREVER; > long renewDuration = RENEW_DURATION; > lrm.renewFor(lease,desiredDuration, renewDuration, new > LeaseListenerMock()); > logger.info("Starting... Press any key to abort"); > System.in.read(); > } > > private final static class MockEventLease extends EventLease { > > MockEventLease(Registrar server, ServiceID registrarID, > long eventID, > Uuid leaseID, long expiration) { > super(server, registrarID, eventID, leaseID, > expiration); > } > > private static final long serialVersionUID = 1L; > > public void cancel() throws UnknownLeaseException, > RemoteException { > logger.info("cancel()"); > } > > protected long doRenew(long duration) > throws UnknownLeaseException, RemoteException > { > long actualDuration = Math.min(duration, > MAX_EVENT_LEASE); > logger.info("doRenew(duration="+duration+") returns " > + actualDuration); > return actualDuration; > } > > static EventLease getInstance(long expiration) > { > return new MockEventLease(null, new > ServiceID(1,1),1,new Uuid(1,1),expiration); > } > } > > private final static class LeaseListenerMock implements > LeaseListener , DesiredExpirationListener { > > /* When lease renewal fails, we discard the proxy */ > public void notify(LeaseRenewalEvent e) { > logger.info("notify(e="+e+")"); > } > > @Override > public void expirationReached(LeaseRenewalEvent e) { > logger.info("expirationReached(e="+e+")"); > > } > } > > private final static class ConfigurationMock extends > AbstractConfiguration { > > private static final String LRM = > "net.jini.lease.LeaseRenewalManager"; > > Map<String,Object> properties = new > HashMap<String,Object>(); > > ConfigurationMock() { > setEntry(LRM, "renewBatchTimeWindow", new > Primitive(RENEW_BATCH_TIME_WINDOW)); > setEntry(LRM, "roundTripTime", new > Primitive(ROUND_TRIP_TIME)); > setEntry(LRM, "taskManager", new TaskManager(11, > 1000 > * 15, 1.0f)); > } > > @Override > protected Object getEntryInternal(String component, > String name, > Class type, Object data) throws > ConfigurationException { > > String key = key(component, name); > if (properties.containsKey(key)) { > return properties.get(key); > } > return data; > } > > private void setEntry(String component, String name, > Object > value) { > properties.put(key(component, name),value); > } > > private String key(String component, String name) { > return component+"."+name; > } > > > } > } >
