Author: toad
Date: 2007-06-23 20:46:04 +0000 (Sat, 23 Jun 2007)
New Revision: 13740

Modified:
   trunk/freenet/src/freenet/io/comm/DMT.java
   trunk/freenet/src/freenet/node/RequestHandler.java
   trunk/freenet/src/freenet/node/RequestSender.java
Log:
Include TIME_LEFT on FNPRecentlyFailed. This is vital to prevent looping, if 
FNPRecentlyFailed's can cause FT entries - which they must, otherwise they will 
not be very helpful in reducing traffic.

Modified: trunk/freenet/src/freenet/io/comm/DMT.java
===================================================================
--- trunk/freenet/src/freenet/io/comm/DMT.java  2007-06-23 19:23:12 UTC (rev 
13739)
+++ trunk/freenet/src/freenet/io/comm/DMT.java  2007-06-23 20:46:04 UTC (rev 
13740)
@@ -112,6 +112,7 @@
        public static final String BWLIMIT_DELAY_TIME = "bwlimitDelayTime";
        public static final String TIME = "time";
        public static final String FORK_COUNT = "forkCount";
+       public static final String TIME_LEFT = "timeLeft";

        //Diagnostic
        public static final MessageType ping = new MessageType("ping") {{
@@ -523,11 +524,13 @@

        public static final MessageType FNPRecentlyFailed = new 
MessageType("FNPRecentlyFailed") {{
                addField(UID, Long.class);
+               addField(TIME_LEFT, Integer.class);
        }};

-       public static final Message createFNPRecentlyFailed(long id) {
+       public static final Message createFNPRecentlyFailed(long id, int 
timeLeft) {
                Message msg = new Message(FNPRecentlyFailed);
                msg.set(UID, id);
+               msg.set(TIME_LEFT, timeLeft);
                return msg;
        }

@@ -711,6 +714,16 @@
                return msg;
        }

+       public static MessageType FNPOfferKey = new MessageType("FNPOfferKey") 
{{
+               addField(KEY, Key.class);
+       }};
+       
+       public static Message createFNPOfferKey(Key key) {
+               Message msg = new Message(FNPOfferKey);
+               msg.set(KEY, key);
+               return msg;
+       }
+       
        public static final MessageType FNPPing = new MessageType("FNPPing") {{
                addField(PING_SEQNO, Integer.class);
        }};

Modified: trunk/freenet/src/freenet/node/RequestHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestHandler.java  2007-06-23 19:23:12 UTC 
(rev 13739)
+++ trunk/freenet/src/freenet/node/RequestHandler.java  2007-06-23 20:46:04 UTC 
(rev 13740)
@@ -146,7 +146,7 @@
                        source.sendSync(dnf, this);
                        return;
                case RequestSender.RECENTLY_FAILED:
-                       Message rf = DMT.createFNPRecentlyFailed(uid);
+                       Message rf = DMT.createFNPRecentlyFailed(uid, 
rs.getRecentlyFailedTimeLeft());
                        source.sendSync(rf, this);
                        return;
                case RequestSender.GENERATED_REJECTED_OVERLOAD:

Modified: trunk/freenet/src/freenet/node/RequestSender.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestSender.java   2007-06-23 19:23:12 UTC 
(rev 13739)
+++ trunk/freenet/src/freenet/node/RequestSender.java   2007-06-23 20:46:04 UTC 
(rev 13740)
@@ -154,6 +154,11 @@

             Message req = createDataRequest();

+            // Not possible to get an accurate time for sending, guaranteed to 
be not later than the time of receipt.
+            // Why? Because by the time the sent() callback gets called, it 
may already have been acked, under heavy load.
+            // So take it from when we first started to try to send the 
request.
+            // See comments below when handling FNPRecentlyFailed for why we 
need this.
+            long timeSentRequest = System.currentTimeMillis();

             next.sendSync(req, this);

@@ -277,7 +282,65 @@

                if(msg.getSpec() == DMT.FNPRecentlyFailed) {
                        next.successNotOverload();
-                       finish(RECENTLY_FAILED, next);
+                       /*
+                        * Must set a correct recentlyFailedTimeLeft before 
calling this finish(), because it will be
+                        * passed to the handler.
+                        * 
+                        * It is *VITAL* that the TIME_LEFT we pass on is not 
larger than it should be.
+                        * It is somewhat less important that it is not too 
much smaller than it should be.
+                        * 
+                        * Why? Because:
+                        * 1) We have to use FNPRecentlyFailed to create 
failure table entries. Because otherwise,
+                        * the failure table is of little value: A request is 
routed through a node, which gets a DNF,
+                        * and adds a failure table entry. Other requests then 
go through that node via other paths.
+                        * They are rejected with FNPRecentlyFailed - not with 
DataNotFound. If this does not create
+                        * failure table entries, more requests will be 
pointlessly routed through that chain.
+                        * 
+                        * 2) If we use a fixed timeout on receiving 
FNPRecentlyFailed, they can be self-seeding. 
+                        * What this means is A sends a request to B, which 
DNFs. This creates a failure table entry 
+                        * which lasts for 10 minutes. 5 minutes later, A sends 
another request to B, which is killed
+                        * with FNPRecentlyFailed because of the failure table 
entry. B's failure table lasts for 
+                        * another 5 minutes, but A's lasts for the full 10 
minutes i.e. until 5 minutes after B's. 
+                        * After B's failure table entry has expired, but 
before A's expires, B sends a request to A. 
+                        * A replies with FNPRecentlyFailed. Repeat ad 
infinitum: A reinforces B's blocks, and B 
+                        * reinforces A's blocks!
+                        * 
+                        * 3) This can still happen even if we check where the 
request is coming from. A loop could 
+                        * very easily form: A - B - C - A. A requests from B, 
DNFs (assume the request comes in from 
+                        * outside, there are more nodes. C requests from A, 
sets up a block. B's block expires, C's 
+                        * is still active. A requests from B which requests 
from C ... and it goes round again.
+                        * 
+                        * 4) It is exactly the same if we specify a timeout, 
unless the timeout can be guaranteed to 
+                        * not increase the expiry time.
+                        */
+                       
+                       // First take the original TIME_LEFT. This will start 
at 10 minutes if we get rejected in
+                       // the same millisecond as the failure table block was 
added.
+                       int timeLeft = msg.getInt(DMT.TIME_LEFT);
+                       int origTimeLeft = timeLeft;
+                       
+                       if(timeLeft <= 0) {
+                               Logger.error(this, "Impossible: 
timeLeft="+timeLeft);
+                               origTimeLeft = 0;
+                               timeLeft=1000; // arbitrary default...
+                       }
+                       
+                       // This is in theory relative to when the request was 
received by the node. Lets make it relative
+                       // to a known event before that: the time when we sent 
the request.
+                       
+                       long timeSinceSent = Math.max(0, 
(System.currentTimeMillis() - timeSentRequest));
+                       timeLeft -= timeSinceSent;
+                       
+                       // Subtract 1% for good measure / to compensate for 
dodgy clocks
+                       timeLeft -= origTimeLeft / 100;
+                       
+                       if(timeLeft <= 0) {
+                               // No timeout left, cool
+                               finish(DATA_NOT_FOUND, next);
+                       } else {
+                               // Some timeout left
+                               finish(RECENTLY_FAILED, next);
+                       }
                        return;
                }

@@ -609,4 +672,10 @@
        public void sentPayload(int x) {
                node.sentPayload(x);
        }
+       
+       private int recentlyFailedTimeLeft;
+
+       synchronized int getRecentlyFailedTimeLeft() {
+               return recentlyFailedTimeLeft;
+       }
 }


Reply via email to