Click here to Skip to main content
15,883,750 members
Articles / Desktop Programming / MFC

.NET Dynamic Software Load Balancing

Rate me:
Please Sign up or sign in to vote.
4.96/5 (111 votes)
9 Dec 200271 min read 510.6K   4.5K   242  
A Draft Implementation of an Idea for .NET Dynamic Software Load Balancing
//////////////////////////////////////////////////////////////////////////////
// .NET Dynamic Software Load Balancing
// A Draft Implementation of an Idea for .NET Dynamic Software Load Balancing
// (c) 2002, Stoyan Damov (stoyan_damov@hotmail.com)
// The software comes �AS IS�, with all faults and no warranties.
//////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MachineLoadsCollection.h"
#include "LoadMapping.h"

namespace SoftwareLoadBalancing
{

MachineLoadsCollection::MachineLoadsCollection (int reportTimeout) :
	Tracer (S"MachineLoadsCollection")
{
	Trace (S"Initializing machine load storage...");
	
	this->reportTimeout = reportTimeout;
	this->loads = new SortedList ();
	this->mappings = new Hashtable ();
	this->rwLock = new ReaderWriterLock ();
	
	// create the Grim Reaper timer and start it immediately
	//
	grimReaper = new Timer (
		new TimerCallback (this, &MachineLoadsCollection::GrimReaper),
		this,
		Timeout::Infinite,
		Timeout::Infinite);
	StartGrimReaper ();
	
	Trace (S"Initialization done.");
}
	
	
MachineLoadsCollection::~MachineLoadsCollection ()
{
	StopGrimReaper ();
}
	

void MachineLoadsCollection::Add (MachineLoad __gc* machineLoad)
{
	DEBUG_ASSERT (0 != machineLoad);
	if (0 == machineLoad)
		return;

	String __gc* name = machineLoad->Name;
	double load = machineLoad->Load;
	Object __gc* boxedLoad = __box (load);

	/////////////////////////////////////////////////
	rwLock->AcquireWriterLock (Timeout::Infinite);
	/////////////////////////////////////////////////

	// a list of all machines that have reported this particular
	// load value
	//
	ArrayList __gc* loadList = 0;

	// check whether any machine has reported such a load
	//
	if (!loads->ContainsKey (boxedLoad))
	{
		// no, this is the first load of a kind
		//
		loadList = new ArrayList ();
		// add the list to the main loads (sorted) list
		//
		loads->Add (boxedLoad, loadList);
	}
	else
	{
		// yes, one or more machines reported the same load already
		//
		loadList = static_cast<ArrayList __gc*> (
			loads->get_Item (boxedLoad));
	}

	// check if this machine has already reported a load previously
	//
	if (!mappings->ContainsKey (name))
	{
		// no, the machine is reporting for the first time
		// insert the element and update the mappings
		//
		loadList->Add (machineLoad);
		mappings->Add (name, new LoadMapping (machineLoad, loadList));
	}
	else
	{
		// yes, the machine has reported its load before
		// we should remove the old load; get its mapping
		//
		LoadMapping __gc* mappedLoad = 
			static_cast<LoadMapping __gc*> (mappings->get_Item (name));

		// get the old load, and the list we should remove it from
		//
		MachineLoad __gc* oldLoad = mappedLoad->Load;
		ArrayList __gc* oldList = mappedLoad->LoadList;

		// remove the old mapping
		//
		mappings->Remove (name);

		// remove the old load from the old list
		//
		int index = oldList->IndexOf (oldLoad);
		oldList->RemoveAt (index);

		// insert the new load into the new list
		//
		loadList->Add (machineLoad);
		
		// update the mappings
		//
		mappings->Add (name, new LoadMapping (machineLoad, loadList));

		// finally, check if the old load list is totally empty
		// and if so, remove it from the main (sorted) list
		//
		if (oldList->Count == 0)
			loads->Remove (__box (oldLoad->Load));
	}

	/////////////////////////////////////////////////
	rwLock->ReleaseWriterLock ();
	/////////////////////////////////////////////////

	MiniTrace (S"+");
}

MachineLoad __gc* MachineLoadsCollection::get_MinimumLoad ()
{
	MachineLoad __gc* load = 0;

	/////////////////////////////////////////////////
	rwLock->AcquireReaderLock (Timeout::Infinite);
	/////////////////////////////////////////////////

	// if the collection is empty, no machine has reported
	// its machineLoad, so we return "null"
	//
	if (loads->Count > 0)
	{

		// the 1st element should contain one of the least 
		// loaded machines -- they all have the same load
		// in this list
		//
		ArrayList __gc* minLoadedMachines = 
			static_cast<ArrayList __gc*> (loads->GetByIndex (0));
		load = static_cast<MachineLoad __gc*> (
			minLoadedMachines->get_Item (0));
	}

	/////////////////////////////////////////////////
	rwLock->ReleaseReaderLock ();
	/////////////////////////////////////////////////

	return (load);
}
	
void MachineLoadsCollection::StartGrimReaper ()
{
	keepGrimReaperAlive = true;
	grimReaper->Change (0, reportTimeout);
}

void MachineLoadsCollection::StopGrimReaper ()
{
	keepGrimReaperAlive = false;
	// GrimReaper will automatically suspend after its
	// last callback invocation
}

void MachineLoadsCollection::GrimReaper (Object __gc* state)
{
	// get the state we need to continue
	//
	MachineLoadsCollection __gc* mlc = 
		static_cast<MachineLoadsCollection __gc*> (state);
	// temporarily suspend the timer
	//
	mlc->grimReaper->Change (
		Timeout::Infinite, 
		Timeout::Infinite);
	// check if we are forced to stop
	//
	if (!mlc->keepGrimReaperAlive)
		return;
	// get the rest of the fields to do our work
	//
	ReaderWriterLock __gc* rwLock = mlc->rwLock;
	SortedList __gc* loads = mlc->loads;
	Hashtable __gc* mappings = mlc->mappings;
	int reportTimeout = mlc->reportTimeout;
	
	////////////////////////////////////////////////
	rwLock->AcquireWriterLock (Timeout::Infinite);
	////////////////////////////////////////////////

	// Bring out the dead :)
	//
	
	// enumerating via an IDictionaryEnumerator, we can't delete
	// elements from the hashtable mappings; so we create a temporary
	// list of machines for deletion, and delete them after we have
	// finished with the enumeration
	//
	StringCollection __gc* deadMachines = new StringCollection ();
	
	// walk the mappings to get all machines
	//
	DateTime dtNow = DateTime::Now;
	IDictionaryEnumerator __gc* dic = mappings->GetEnumerator ();
	while (dic->MoveNext ())
	{
		LoadMapping __gc* map = 
			static_cast<LoadMapping __gc*> (dic->Value);
		// check whether the dead timeout has expired for this machine
		//
		TimeSpan tsDifference = dtNow.Subtract (map->LastReport);
		double difference = tsDifference.TotalMilliseconds;
		if (difference > (double) reportTimeout)
		{
			// remove the machine from the data structures; it is
			// now considered dead and does not participate anymore
			// in the load balancing, unless it reports its load
			// at some later time
			//
			String __gc* name = map->Load->Name;

			// get the old load, and the list we should remove it from
			//
			MachineLoad __gc* oldLoad = map->Load;
			ArrayList __gc* oldList = map->LoadList;

			// remove the old mapping (only add it to the deletion list)
			//
			deadMachines->Add (name);

			// remove the old load from the old list
			//
			int index = oldList->IndexOf (oldLoad);
			oldList->RemoveAt (index);

			// finally, check if the old load list is totally empty
			// and if so, remove it from the main list
			//
			if (oldList->Count == 0)
				loads->Remove (__box (oldLoad->Load));
			
			MiniTrace (S"^");
		}
	}
	
	// actually remove the dead machines from the mappings
	//
	for (int i=0; i<deadMachines->Count; i++)
		mappings->Remove (deadMachines->get_Item (i));
	
	// cleanup
	//
	deadMachines->Clear ();
	
	////////////////////////////////////////////////
	rwLock->ReleaseWriterLock ();
	////////////////////////////////////////////////
	
	// resume the timer
	//
	mlc->grimReaper->Change (
		reportTimeout, 
		reportTimeout);
}

void MachineLoadsCollection::Dump ()
{
	/////////////////////////////////////////////////
	rwLock->AcquireReaderLock (Timeout::Infinite);
	/////////////////////////////////////////////////

	Console::WriteLine (S"\n{0}", new String (L'=', 60));
	for (int i=0; i<loads->Count; i++)
	{
		ArrayList __gc* machines = 
			static_cast<ArrayList __gc*> (loads->GetByIndex (i));
		if (machines->Count > 0)
		{
			MachineLoad __gc* load = static_cast<MachineLoad __gc*> (
				machines->get_Item (0));
			Console::WriteLine (S"Machines of load {0}", __box (load->Load));
			for (int j=0; j<machines->Count; j++)
			{
				load = static_cast<MachineLoad __gc*> (
					machines->get_Item (j));
				Console::WriteLine (S"\t{0}", load->Name);
			}
		}
	}
	Console::WriteLine (S"\n{0}", new String (L'-', 60));
	MachineLoad __gc* minLoad = MinimumLoad;
	if (0 != minLoad)
	{
		Console::WriteLine (S"(One of) the fastest machine(s) is {0}",
			minLoad->Name);
	}

	/////////////////////////////////////////////////
	rwLock->ReleaseReaderLock ();
	/////////////////////////////////////////////////
}

} // namespace

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Bulgaria Bulgaria
I'm crazy about programming, bleeding-edge technologies and my wife, Irina. Thinking seriously to start living in Centurian time.

The image shows me, happy :P

My blog

Comments and Discussions