Add the new NumGen
Added the new and distributed NumGen, which replaces the old one. The old implementation was based on a master server.
This commit is contained in:
		
							parent
							
								
									6dab89a1d4
								
							
						
					
					
						commit
						81c4d15d9f
					
				| @ -42,9 +42,6 @@ func checkConfiguration() { | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`LogStaticFileRequests`, `false`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseDatabaseLogging`, `false`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseConsoleLogging`, `true`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenActiveHosts`, `please replace this with the correct hostname of the host which is the master number generator`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenGetHandler`, `http://localhost:80/next/number`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenBufferSize`, `12`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`OceanUtilizeCPUs`, `2`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`FilenameWebResources`, `web.zip`) | ||||
| 	CheckSingleConfigurationPresentsAndAddIfMissing(`MapStaticFiles2Root`, `false`) | ||||
|  | ||||
| @ -1,6 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| func BadNumber() (result int64) { | ||||
| 	result = badNumber64 | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										71
									
								
								NumGen/Doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								NumGen/Doc.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| /* | ||||
| NumGen is a distributed number generator which generates unique | ||||
| numbers at a distributed sysem without any centralised or master server. | ||||
| 
 | ||||
| Range of 64 bit integer: -9223372036854775808 til 9223372036854775807 | ||||
| 
 | ||||
| Construction of the ID: | ||||
| ----------------------- | ||||
| 
 | ||||
| First comes the time part: | ||||
| 
 | ||||
| -9223372036854775808 (base number is the smallest possible int64) | ||||
| +YYYY * 1000000000000000 | ||||
| +  MM * 10000000000000 | ||||
| +  DD * 100000000000 | ||||
| +  HH * 1000000000 | ||||
| +  mm * 10000000 | ||||
| +  ss * 100000 | ||||
| + fff * 100 | ||||
| 
 | ||||
| 
 | ||||
| Second the machine part: | ||||
| 
 | ||||
| + 1000000000000000000 (offset for the machine part to get a fixed length) | ||||
| + PID (capped on 822336) * 10000000000000 | ||||
| + Num CPUs (capped on 99) * 100000000000 | ||||
| + PageSize (capped on 999999) * 100000 | ||||
| + Random 0-99 * 1000 | ||||
| + Sequence 0-999 * 1 | ||||
| 
 | ||||
| 
 | ||||
| Positions: | ||||
| ---------- | ||||
| 
 | ||||
| First part: | ||||
| 
 | ||||
|    9999 maximum year | ||||
|        12 month | ||||
|          31 day | ||||
|            24 hours | ||||
|              59 minutes | ||||
|                59 seconds | ||||
|                  999 milliseconds | ||||
|    99991231245959999 Maximum | ||||
| -9223372036854775808 Base value | ||||
| 
 | ||||
| 
 | ||||
| Second part: | ||||
| 
 | ||||
|   822336 PID | ||||
|         99 CPUs | ||||
|           999999 Page Size | ||||
|                 99 Random | ||||
|                   999 Sequence | ||||
|   8223369999999999999 Maximum content value | ||||
|  +1000000000000000000 Offset=Minimum | ||||
|   9223369999999999999 Maximum of part two | ||||
| 
 | ||||
| 
 | ||||
| The number at the year 9999, if all IDs are used, is: | ||||
| 99989194391184190 | ||||
| 
 | ||||
| Therefore, this is the reserve for a further implementation: | ||||
| 9223372036854775807 - 99989194391184190 = 9123382842463591617 | ||||
| 
 | ||||
| Test & Development: | ||||
| http://play.golang.org/p/-wbvmFV99D
 | ||||
| 
 | ||||
| */ | ||||
							
								
								
									
										69
									
								
								NumGen/GenerateMachineID.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								NumGen/GenerateMachineID.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"math/big" | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| ) | ||||
| 
 | ||||
| // Internal function to generate the second part of the ID.
 | ||||
| func generateMachineID() (id int64) { | ||||
| 
 | ||||
| 	//
 | ||||
| 	// Please have a look to the main documentation of this package,
 | ||||
| 	// if you are interested about how to calculate the parts.
 | ||||
| 	//
 | ||||
| 
 | ||||
| 	// Using for all calculations big integers to get a precise result!
 | ||||
| 
 | ||||
| 	bigResult := new(big.Int) | ||||
| 	bigBase := new(big.Int) | ||||
| 	bigNext := new(big.Int) | ||||
| 	bigBase.SetString("1000000000000000000", 10) | ||||
| 	bigResult = bigBase | ||||
| 
 | ||||
| 	pid := int64(os.Getpid()) | ||||
| 	cpus := int64(runtime.NumCPU()) | ||||
| 	pageSize := int64(os.Getpagesize()) | ||||
| 	rnd := int64(rand.Intn(100)) | ||||
| 
 | ||||
| 	if pid > 822336 { | ||||
| 		pid = int64(822336) | ||||
| 	} | ||||
| 
 | ||||
| 	if cpus > 99 { | ||||
| 		cpus = int64(99) | ||||
| 	} | ||||
| 
 | ||||
| 	if pageSize > 999999 { | ||||
| 		pageSize = int64(999999) | ||||
| 	} | ||||
| 
 | ||||
| 	bigNext = big.NewInt(pid) | ||||
| 	bigPID := new(big.Int) | ||||
| 	bigPID.SetString("10000000000000", 10) | ||||
| 	bigPID = bigPID.Mul(bigPID, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigPID) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(cpus) | ||||
| 	bigCPU := new(big.Int) | ||||
| 	bigCPU.SetString("100000000000", 10) | ||||
| 	bigCPU = bigCPU.Mul(bigCPU, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigCPU) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(pageSize) | ||||
| 	bigPage := new(big.Int) | ||||
| 	bigPage.SetString("100000", 10) | ||||
| 	bigPage = bigPage.Mul(bigPage, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigPage) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(rnd) | ||||
| 	bigRND := new(big.Int) | ||||
| 	bigRND.SetString("1000", 10) | ||||
| 	bigRND = bigRND.Mul(bigRND, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigRND) | ||||
| 
 | ||||
| 	id = bigResult.Int64() | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										77
									
								
								NumGen/GenerateTimeID.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								NumGen/GenerateTimeID.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"math/big" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Internal function to generate the first part of the ID.
 | ||||
| func generateTimeID() (id int64) { | ||||
| 
 | ||||
| 	//
 | ||||
| 	// Please have a look to the main documentation of this package,
 | ||||
| 	// if you are interested about how to calculate the parts.
 | ||||
| 	//
 | ||||
| 
 | ||||
| 	// Using for all calculations big integers to get a precise result!
 | ||||
| 
 | ||||
| 	bigResult := new(big.Int) | ||||
| 	bigBase := new(big.Int) | ||||
| 	bigNext := new(big.Int) | ||||
| 	bigBase.SetString("-9223372036854775808", 10) | ||||
| 	bigResult = bigBase | ||||
| 
 | ||||
| 	t1 := time.Now().UTC() | ||||
| 	year := int64(t1.Year()) | ||||
| 	month := int64(t1.Month()) | ||||
| 	day := int64(t1.Day()) | ||||
| 	hours := int64(t1.Hour()) | ||||
| 	minutes := int64(t1.Minute()) | ||||
| 	seconds := int64(t1.Second()) | ||||
| 	milliseconds := int64(float64(t1.Nanosecond()) / 1000000.0) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(year) | ||||
| 	bigYear := new(big.Int) | ||||
| 	bigYear.SetString("1000000000000000", 10) | ||||
| 	bigYear = bigYear.Mul(bigYear, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigYear) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(month) | ||||
| 	bigMonth := new(big.Int) | ||||
| 	bigMonth.SetString("10000000000000", 10) | ||||
| 	bigMonth = bigMonth.Mul(bigMonth, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigMonth) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(day) | ||||
| 	bigDay := new(big.Int) | ||||
| 	bigDay.SetString("100000000000", 10) | ||||
| 	bigDay = bigDay.Mul(bigDay, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigDay) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(hours) | ||||
| 	bigHours := new(big.Int) | ||||
| 	bigHours.SetString("1000000000", 10) | ||||
| 	bigHours = bigHours.Mul(bigHours, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigHours) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(minutes) | ||||
| 	bigMinutes := new(big.Int) | ||||
| 	bigMinutes.SetString("10000000", 10) | ||||
| 	bigMinutes = bigMinutes.Mul(bigMinutes, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigMinutes) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(seconds) | ||||
| 	bigSeconds := new(big.Int) | ||||
| 	bigSeconds.SetString("100000", 10) | ||||
| 	bigSeconds = bigSeconds.Mul(bigSeconds, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigSeconds) | ||||
| 
 | ||||
| 	bigNext = big.NewInt(milliseconds) | ||||
| 	bigMilliseconds := new(big.Int) | ||||
| 	bigMilliseconds.SetString("100", 10) | ||||
| 	bigMilliseconds = bigMilliseconds.Mul(bigMilliseconds, bigNext) | ||||
| 	bigResult = bigResult.Add(bigResult, bigMilliseconds) | ||||
| 
 | ||||
| 	id = bigResult.Int64() | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										39
									
								
								NumGen/GenerateUniqueID.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								NumGen/GenerateUniqueID.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"math/big" | ||||
| ) | ||||
| 
 | ||||
| // Internal function to generate a unique ID with the given sequenceNumber.
 | ||||
| func generateUniqueID(sequenceNumber int) (id int64) { | ||||
| 
 | ||||
| 	// Ensure, that the sequenceNumber is not smaller than 0:
 | ||||
| 	if sequenceNumber < 0 { | ||||
| 		sequenceNumber = sequenceNumber * -1 | ||||
| 	} | ||||
| 
 | ||||
| 	// Ensure, that the sequenceNumber is not bigger than 999:
 | ||||
| 	if sequenceNumber > 999 { | ||||
| 		sequenceNumber = 999 | ||||
| 	} | ||||
| 
 | ||||
| 	// Convert the sequenceNumber to a 64 bit integer:
 | ||||
| 	seq := int64(sequenceNumber) | ||||
| 
 | ||||
| 	// Generate the first and second part of the ID:
 | ||||
| 	timeID := generateTimeID()       // First part
 | ||||
| 	machineID := generateMachineID() // Second part
 | ||||
| 
 | ||||
| 	// Convert all numbers to big integers:
 | ||||
| 	bigSEQ := big.NewInt(seq) | ||||
| 	bigMachineID := big.NewInt(machineID) | ||||
| 	bigTimeID := big.NewInt(timeID) | ||||
| 
 | ||||
| 	// Add the parts to get the result:
 | ||||
| 	bigResult := bigTimeID.Add(bigTimeID, bigMachineID) | ||||
| 	bigResult = bigResult.Add(bigResult, bigSEQ) | ||||
| 
 | ||||
| 	// The result as 64 bit integer:
 | ||||
| 	id = bigResult.Int64() | ||||
| 	return | ||||
| } | ||||
| @ -1,32 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/SommerEngineering/Ocean/Log" | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"github.com/SommerEngineering/Ocean/Shutdown" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strconv" | ||||
| ) | ||||
| 
 | ||||
| func GetNextInt64(name string) (result int64) { | ||||
| 	result = badNumber64 | ||||
| 
 | ||||
| 	if Shutdown.IsDown() { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if responseData, errRequest := http.PostForm(getHandler, url.Values{"name": {name}, "password": {correctPassword}}); errRequest != nil { | ||||
| 		Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameGENERATOR, `Requesting the next number was not possible.`, errRequest.Error()) | ||||
| 		return | ||||
| 	} else { | ||||
| 		nextNumberText := responseData.Header.Get(`nextNumber`) | ||||
| 		if number, errAtio := strconv.Atoi(nextNumberText); errAtio != nil { | ||||
| 			Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameGENERATOR, `It was not possible to convert the answer into an int64.`, errAtio.Error()) | ||||
| 			return | ||||
| 		} else { | ||||
| 			result = int64(number) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										71
									
								
								NumGen/GetUniqueID.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								NumGen/GetUniqueID.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Function to generate a unique ID.
 | ||||
| func GetUniqueID() (id int64) { | ||||
| 
 | ||||
| 	// Exklusive access:
 | ||||
| 	genLock.Lock() | ||||
| 	defer genLock.Unlock() | ||||
| 	incremented := false | ||||
| 
 | ||||
| Retry: | ||||
| 
 | ||||
| 	// Read the current time
 | ||||
| 	t1 := time.Now().UTC() | ||||
| 	t1Milliseconds := int(float64(t1.Nanosecond()) / 1000000.0) | ||||
| 
 | ||||
| 	// Calculate the difference to the last generated number:
 | ||||
| 	diff := t1.Sub(genCurrentTime) | ||||
| 
 | ||||
| 	// Case 1: Huge difference?
 | ||||
| 	if diff.Seconds() > 1.0 { | ||||
| 		genCurrentTime = t1 | ||||
| 		genCurrentMillisecond = t1Milliseconds | ||||
| 		genCurrentMillisecondCounter = 0 | ||||
| 		id = generateUniqueID(genCurrentMillisecondCounter) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	/* | ||||
| 		The fist case is necessary, because the same count of milliseconds | ||||
| 		can occur every second, etc. ;-) Therefore, a check only by the | ||||
| 		milliseconds is not sufficient. | ||||
| 	*/ | ||||
| 
 | ||||
| 	// Case 2: Small difference?
 | ||||
| 	if t1Milliseconds != genCurrentMillisecond { | ||||
| 		genCurrentTime = t1 | ||||
| 		genCurrentMillisecond = t1Milliseconds | ||||
| 		genCurrentMillisecondCounter = 0 | ||||
| 		id = generateUniqueID(genCurrentMillisecondCounter) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	//
 | ||||
| 	// Case 3: Another number must be generated at the same millisecond!
 | ||||
| 	//
 | ||||
| 
 | ||||
| 	if incremented == false { | ||||
| 		genCurrentMillisecondCounter++ | ||||
| 		incremented = true | ||||
| 	} | ||||
| 
 | ||||
| 	// Case 3.1: More than 1000 numbers are generated at one millisecond?
 | ||||
| 	if genCurrentMillisecondCounter > 999 { | ||||
| 		// This is not possible with the current algorithm!
 | ||||
| 		// Therefore, we force this and all other request to wait some time:
 | ||||
| 		time.Sleep(1 * time.Millisecond) | ||||
| 
 | ||||
| 		// Try it again:
 | ||||
| 		goto Retry | ||||
| 		// Case 3.2: Less than 1000 numbers are generated at one millisecond?
 | ||||
| 	} else { | ||||
| 		// This case is fine:
 | ||||
| 		id = generateUniqueID(genCurrentMillisecondCounter) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/SommerEngineering/Ocean/Log" | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"github.com/SommerEngineering/Ocean/Shutdown" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func HandlerGetNext(response http.ResponseWriter, request *http.Request) { | ||||
| 	if Shutdown.IsDown() { | ||||
| 		http.NotFound(response, request) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !isActive { | ||||
| 		Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `Called the get handler on an inactive host.`, `Wrong configuration?`) | ||||
| 		http.NotFound(response, request) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if correctPassword == `` { | ||||
| 		Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameSECURITY, `No communication password was set.`) | ||||
| 		http.NotFound(response, request) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	name := request.FormValue(`name`) | ||||
| 	pwd := request.FormValue(`password`) | ||||
| 
 | ||||
| 	if pwd != correctPassword { | ||||
| 		Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactNone, LM.MessageNamePASSWORD, `A wrong password was used to access this system handler.`, `This should never happens: Is this a hacking attempt?`, `IP address of requester=`+request.RemoteAddr) | ||||
| 		http.NotFound(response, request) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelDEBUG, LM.MessageNameANALYSIS, `Next number requested.`, name, pwd) | ||||
| 	channel := requestChannel4Name(name) | ||||
| 	nextNumber := <-channel | ||||
| 
 | ||||
| 	response.Header().Add(`nextNumber`, fmt.Sprintf(`%d`, nextNumber)) | ||||
| } | ||||
| @ -1,42 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/SommerEngineering/Ocean/ConfigurationDB" | ||||
| 	"github.com/SommerEngineering/Ocean/Log" | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"github.com/SommerEngineering/Ocean/Tools" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| // Init this package.
 | ||||
| func init() { | ||||
| 	Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Init the number generator.`) | ||||
| 
 | ||||
| 	// Get the exklusive access to the channel list:
 | ||||
| 	channelListLock.Lock() | ||||
| 	defer channelListLock.Unlock() | ||||
| 
 | ||||
| 	correctPassword = ConfigurationDB.Read(`InternalCommPassword`) | ||||
| 	activeHost := ConfigurationDB.Read(`NumGenActiveHosts`) | ||||
| 	isActive = strings.Contains(activeHost, Tools.ThisHostname()) | ||||
| 	getHandler = ConfigurationDB.Read(`NumGenGetHandler`) | ||||
| 
 | ||||
| 	if isActive { | ||||
| 		Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.MessageNameCONFIGURATION, `The number generator is active on this host.`, `This host is producer and consumer.`) | ||||
| 
 | ||||
| 		channelBufferSizeText := ConfigurationDB.Read(`NumGenBufferSize`) | ||||
| 		if bufferSizeNumber, errBufferSizeNumber := strconv.Atoi(channelBufferSizeText); errBufferSizeNumber != nil { | ||||
| 			Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactMiddle, LM.MessageNameCONFIGURATION, `Was not able to parse the configuration value of NumGenBufferSize.`, errBufferSizeNumber.Error(), `Use the default value now!`) | ||||
| 		} else { | ||||
| 			channelBufferSize = bufferSizeNumber | ||||
| 			Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The buffer size for the number generator was loaded.`, `Buffer size=`+channelBufferSizeText) | ||||
| 		} | ||||
| 
 | ||||
| 		channelList = make(map[string]chan int64) | ||||
| 
 | ||||
| 		initDB() | ||||
| 	} else { | ||||
| 		Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.MessageNameCONFIGURATION, `The number generator is not active on this host.`, `This host is just a consumer.`) | ||||
| 	} | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/SommerEngineering/Ocean/CustomerDB" | ||||
| 	"github.com/SommerEngineering/Ocean/Log" | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"gopkg.in/mgo.v2" | ||||
| ) | ||||
| 
 | ||||
| func initDB() { | ||||
| 	Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of number generator collection.`) | ||||
| 	defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init of number generator collection.`) | ||||
| 
 | ||||
| 	// Get the database:
 | ||||
| 	dbSession, db = CustomerDB.DB() | ||||
| 
 | ||||
| 	if db == nil { | ||||
| 		Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Get my collection:
 | ||||
| 	collectionNumGen = db.C(`NumGen`) | ||||
| 
 | ||||
| 	// Take care about the indexes:
 | ||||
| 	indexName := mgo.Index{} | ||||
| 	indexName.Key = []string{`Name`} | ||||
| 	indexName.Unique = true | ||||
| 	collectionNumGen.EnsureIndex(indexName) | ||||
| } | ||||
| @ -1,70 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/SommerEngineering/Ocean/Log" | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"github.com/SommerEngineering/Ocean/Shutdown" | ||||
| 	"gopkg.in/mgo.v2/bson" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func producer(name string) { | ||||
| 	Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The NumGen producer is now starting.`, `name=`+name) | ||||
| 
 | ||||
| 	// Get my channel:
 | ||||
| 	myChannel := requestChannel4Name(name) | ||||
| 
 | ||||
| 	// Read my next free number:
 | ||||
| 	currentNextFreeNumber := nextFreeNumberFromDatabase(name) | ||||
| 
 | ||||
| 	// Where is the next "reload"?
 | ||||
| 	nextReload := currentNextFreeNumber + int64(channelBufferSize) | ||||
| 
 | ||||
| 	// Set the next free number to the database:
 | ||||
| 	updateNextFreeNumber2Database(name, nextReload) | ||||
| 
 | ||||
| 	Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The NumGen producer is now running.`, `name=`+name) | ||||
| 	for nextNumber := currentNextFreeNumber; true; { | ||||
| 		if Shutdown.IsDown() { | ||||
| 			Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSHUTDOWN, `The NumGen producer is now down.`, `name=`+name) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		if nextNumber > nextReload { | ||||
| 			nextReload = nextReload + int64(channelBufferSize) | ||||
| 			updateNextFreeNumber2Database(name, nextReload) | ||||
| 
 | ||||
| 			// Enables the administrator to monitor the frequence of chunks and is able to reconfigure the settings:
 | ||||
| 			Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelDEBUG, LM.MessageNamePRODUCER, `The NumGen producer creates the next chunk.`, `name=`+name) | ||||
| 		} | ||||
| 
 | ||||
| 		// Enqueue the next number:
 | ||||
| 		select { | ||||
| 		case myChannel <- nextNumber: | ||||
| 			nextNumber++ | ||||
| 		case <-time.After(time.Millisecond * 500): | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func nextFreeNumberFromDatabase(name string) (result int64) { | ||||
| 	selection := bson.D{{`Name`, name}} | ||||
| 	searchResult := NumberGenScheme{} | ||||
| 
 | ||||
| 	count, _ := collectionNumGen.Find(selection).Count() | ||||
| 	if count == 1 { | ||||
| 		collectionNumGen.Find(selection).One(&searchResult) | ||||
| 		result = searchResult.NextFreeNumber | ||||
| 	} else { | ||||
| 		searchResult.Name = name | ||||
| 		searchResult.NextFreeNumber = startValue64 | ||||
| 		collectionNumGen.Insert(searchResult) | ||||
| 		result = searchResult.NextFreeNumber | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func updateNextFreeNumber2Database(name string, nextFreeNumber int64) { | ||||
| 	selection := bson.D{{`Name`, name}} | ||||
| 	collectionNumGen.Update(selection, NumberGenScheme{Name: name, NextFreeNumber: nextFreeNumber}) | ||||
| } | ||||
| @ -1,40 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/SommerEngineering/Ocean/Log" | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"github.com/SommerEngineering/Ocean/Shutdown" | ||||
| ) | ||||
| 
 | ||||
| func requestChannel4Name(name string) (result chan int64) { | ||||
| 
 | ||||
| 	if Shutdown.IsDown() { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !isActive { | ||||
| 		Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `Called the requestChannel4Name() on an inactive host.`, `Wrong configuration?`) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	channelListLock.RLock() | ||||
| 	channel, isPresent := channelList[name] | ||||
| 	channelListLock.RUnlock() | ||||
| 
 | ||||
| 	if isPresent { | ||||
| 		result = channel | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Create the entry:
 | ||||
| 	newChannel := make(chan int64, channelBufferSize) | ||||
| 	result = newChannel | ||||
| 
 | ||||
| 	channelListLock.Lock() | ||||
| 	channelList[name] = newChannel | ||||
| 	channelListLock.Unlock() | ||||
| 
 | ||||
| 	// Create the new producer:
 | ||||
| 	go producer(name) | ||||
| 	return | ||||
| } | ||||
| @ -1,7 +0,0 @@ | ||||
| package NumGen | ||||
| 
 | ||||
| // The scheme for the database.
 | ||||
| type NumberGenScheme struct { | ||||
| 	Name           string `bson:"Name"`           // A name for this counter.
 | ||||
| 	NextFreeNumber int64  `bson:"NextFreeNumber"` // The next number.
 | ||||
| } | ||||
| @ -5,11 +5,11 @@ import ( | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| ) | ||||
| 
 | ||||
| // The type for the shutdown function.
 | ||||
| type ShutdownFunction struct { | ||||
| } | ||||
| 
 | ||||
| // The shutdown handler for this package.
 | ||||
| func (a ShutdownFunction) Shutdown() { | ||||
| 	Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down the number generator.`) | ||||
| 	db.Logout() | ||||
| 	dbSession.Close() | ||||
| } | ||||
|  | ||||
| @ -2,26 +2,14 @@ package NumGen | ||||
| 
 | ||||
| import ( | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"gopkg.in/mgo.v2" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	correctPassword string = `` | ||||
| 
 | ||||
| 	// This is the name for logging event from this package:
 | ||||
| 	senderName        LM.Sender             = `System::NumGen::Producer` | ||||
| 	isActive          bool                  = false | ||||
| 	getHandler        string                = `` | ||||
| 	db                *mgo.Database         = nil | ||||
| 	dbSession         *mgo.Session          = nil | ||||
| 	collectionNumGen  *mgo.Collection       = nil | ||||
| 	channelBufferSize int                   = 10 | ||||
| 	channelList       map[string]chan int64 = nil | ||||
| 	channelListLock   sync.RWMutex          = sync.RWMutex{} | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	badNumber64  int64 = 9222222222222222222 | ||||
| 	startValue64 int64 = -9223372036854775808 | ||||
| 	senderName                   LM.Sender  = `System::NumGen` // This is the name for logging event from this package
 | ||||
| 	genLock                      sync.Mutex = sync.Mutex{}     // The mutex for the generator
 | ||||
| 	genCurrentTime               time.Time  = time.Now().UTC() // The time for the last generated number
 | ||||
| 	genCurrentMillisecond        int        = 0                // The millisecond for the last generated number
 | ||||
| 	genCurrentMillisecondCounter int        = 0                // The counter of how many numbers are generated at the same time
 | ||||
| ) | ||||
|  | ||||
| @ -9,6 +9,7 @@ Ocean is a smart and powerful application framework and server which uses the KI | ||||
| * A messaging component called ICCC to communicate with all of your servers (or even with other parts at different programming languages at different servers) | ||||
| * A distributed template engine for web applications | ||||
| * A distributed half-automated configuration management | ||||
| * A distributed number generator which produces e.g. customer IDs | ||||
| * A simple I18N support | ||||
| * A simple database abstraction which MongoDB as database back-end | ||||
| 
 | ||||
|  | ||||
| @ -8,7 +8,6 @@ import ( | ||||
| 	"github.com/SommerEngineering/Ocean/Log" | ||||
| 	LM "github.com/SommerEngineering/Ocean/Log/Meta" | ||||
| 	"github.com/SommerEngineering/Ocean/Log/Web" | ||||
| 	"github.com/SommerEngineering/Ocean/NumGen" | ||||
| 	"github.com/SommerEngineering/Ocean/Robots" | ||||
| 	"github.com/SommerEngineering/Ocean/StaticFiles" | ||||
| 	"github.com/SommerEngineering/Ocean/WebContent" | ||||
| @ -30,9 +29,6 @@ func InitHandlers() { | ||||
| 	// Handler for other static files:
 | ||||
| 	Handlers.AddPublicHandler(`/staticFiles/`, StaticFiles.HandlerStaticFiles) | ||||
| 
 | ||||
| 	// Handler for the number generator:
 | ||||
| 	Handlers.AddPublicHandler(`/next/number`, NumGen.HandlerGetNext) | ||||
| 
 | ||||
| 	// Handler for the robots.txt:
 | ||||
| 	Handlers.AddPublicHandler(`/robots.txt`, Robots.HandlerRobots) | ||||
| 
 | ||||
| @ -49,9 +45,6 @@ func InitHandlers() { | ||||
| 	// Handler for other static files:
 | ||||
| 	Handlers.AddAdminHandler(`/staticFiles/`, StaticFiles.HandlerStaticFiles) | ||||
| 
 | ||||
| 	// Handler for the number generator:
 | ||||
| 	Handlers.AddAdminHandler(`/next/number`, NumGen.HandlerGetNext) | ||||
| 
 | ||||
| 	// Handler for the ICCC to the private side:
 | ||||
| 	Handlers.AddAdminHandler(`/ICCC`, ICCC.ICCCHandler) | ||||
| 
 | ||||
|  | ||||
| @ -11,6 +11,12 @@ func RandomInteger(max int) (rnd int) { | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Gets a random 64 bit float between 0.0 and 1.0 (but without 1.0).
 | ||||
| func RandomFloat64() (rnd float64) { | ||||
| 	rnd = rand.Float64() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Gets a random UUID (v4).
 | ||||
| func RandomGUID() (guidString string) { | ||||
| 	guidString = uuid.NewV4().String() | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user