Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Threading;
- using System.Collections.Generic;
- namespace getScLog
- {
- //A simple class the represents a range between two integers
- class Range
- {
- public int start;
- public int end;
- public Range (int start, int end)
- {
- this.start = start;
- this.end = end;
- }
- //We assume end > start
- public int distance ()
- {
- return (this.end - this.start);
- }
- //Just so we can output Range class easily
- public override String ToString ()
- {
- return String.Format ("Range({0},{1})", start, end);
- }
- }
- //We use a class that calls our function one or more times
- //If first tries the full range and if it doesn't work bisects it into smaller ranges
- class Fetcher
- {
- //This is the function we call to do the work. If it fails by throwing a TimeOutException
- //the input range will be divided and the method will called twice.
- //First line is the type, next is an instance of that type;
- public delegate void Runner (Range r);
- Runner call;
- //Number of calls to "call" we have made
- public int calls;
- //This holds the ranges we still need to fetch
- Queue<Range> tasks = new Queue<Range> ();
- //This is the minimum range we will call the delegate with (should be larger than 1)
- public int minRangeDistance = 3;
- public int retries = 0;
- public int maxDelay = 0;
- public int minDelay = 0;
- public int initialDelay = 0;
- //We keep track of any aborted ranges for logging purposes
- List<Range> failed = new List<Range> ();
- int delay;
- public Fetcher (Runner call)
- {
- this.call = call;
- }
- //To fetch something we simply add the range to the queue which will now hold a single element (the total range)
- //We then ask the fetcher to do the hard work
- public void run (int start, int end)
- {
- //TODO: Check min>=initial>=max,retries>=0,minRangeDistance>0
- failed = new List<Range> ();
- delay = initialDelay;
- Add (start, end);
- run ();
- while(failed.Count > 0 && retries>0){
- Console.WriteLine("Trying again");
- retries--;
- foreach(Range r in getFailed()){
- tasks.Enqueue(r);
- }
- run();
- }
- }
- //We use this function for ensure we don't add to small ranges to the queue
- private void Add (int start, int end)
- {
- Range r = new Range (start, end);
- if (r.distance () < minRangeDistance) {
- updatefailed(r);
- } else {
- tasks.Enqueue (new Range (start, end));
- }
- }
- //We have an aborted range. The way fetcher work they should actually come on order
- //so we merge the small ranges that fails and are adjacent
- private void updatefailed(Range r){
- if(failed.Count > 0 && failed[failed.Count-1].end == r.start)
- {
- failed[failed.Count-1].end = r.end;
- }
- else
- {
- failed.Add(r);
- }
- }
- //This function does the scheduling of the calls
- private void run ()
- {
- //While we still have something to fetch
- while (tasks.Count > 0) {
- //Remove the range (in the first iteration the queue will now be empty)
- Range r = tasks.Dequeue ();
- if(calls != 0 && delay >= 0){
- Thread.Sleep(delay);
- }
- calls++;
- try {
- //We try calling the supplied function
- call (r);
- delay = limit(minDelay,delay - delay/4,maxDelay);
- } catch (TimeoutException) {
- //If this fails due to TimeOut, we (maybe) add to requests back to the queue
- //(The add function will not add too small a request)
- delay = limit(minDelay,delay<<1,maxDelay);
- int middle = (r.start + r.end) / 2;
- Add (r.start, middle);
- Add (middle, r.end);
- }
- }
- //At this point all ranges except the ones in failed will have been fetched
- }
- public static int limit(int min, int a, int max){
- if(a<min)
- {
- return min;
- }
- if(a>max)
- {
- return max;
- }
- return a;
- }
- public List<Range> getFailed ()
- {
- return failed;
- }
- }
- //A simple test of our fetcher class
- class MainClass
- {
- public static void Main (string[] args)
- {
- //We define 3 variables we want to fetch (not related to the fetcher but for demonstration)
- List<String> vars = new List<String>() { "var_a", "var_b", "var_c", "var_d" };
- //We add some reliabilities that is here the maximum value that can be fetched without guaranteed
- //failure.
- List<int> reliabilities = new List<int>() { 60, 1200, 90, 2000 };
- //Used to simulate random failures
- Random random = new Random();
- for (int i=0;i<vars.Count;i++) {
- String s = vars[i];
- int reliability = reliabilities[i];
- //For each variable we define a function on how to fetch such a variable giving a range
- //Could be a call to a way more advanced system ;)
- Fetcher.Runner cb = delegate(Range r) {
- //Here we simulate a call to the service with a random failure chance
- //If the reliability is below 1000 we also add a non fecthable area to simulate permenent failures
- if (random.Next(reliability) < r.distance() || (reliability < 1000 && r.end > 800 && r.start < 900)) {
- //Lets writeout when we tell the Fetcher that it cant get the values
- Console.WriteLine ("Could not fetch " + s + " " + r);
- throw new TimeoutException ();
- }
- //...and when we can
- Console.WriteLine ("Got " + s + " " + r);
- };
- // We setup the fetcher
- Fetcher f = new Fetcher (cb);
- f.minRangeDistance = 7;
- f.retries = 1;
- f.maxDelay = 500;
- f.minDelay = 1;
- f.initialDelay = 1;
- // and ask it to get a range
- f.run (1, 1000);
- //Lastly we sum up the ranges that couldn't be fetched
- // Due to the merge of adjacent fails this should be a lot less than the "Could not fetch" messages
- Console.WriteLine ("Used {0} calls to fetch {1}",f.calls,s);
- foreach (Range r in f.getFailed ()) {
- Console.WriteLine ("Failed to get {0}", r);
- }
- }
- }
- }
- }
Add Comment
Please, Sign In to add comment