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(`LogStaticFileRequests`, `false`)
|
||||||
CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseDatabaseLogging`, `false`)
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseDatabaseLogging`, `false`)
|
||||||
CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseConsoleLogging`, `true`)
|
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(`OceanUtilizeCPUs`, `2`)
|
||||||
CheckSingleConfigurationPresentsAndAddIfMissing(`FilenameWebResources`, `web.zip`)
|
CheckSingleConfigurationPresentsAndAddIfMissing(`FilenameWebResources`, `web.zip`)
|
||||||
CheckSingleConfigurationPresentsAndAddIfMissing(`MapStaticFiles2Root`, `false`)
|
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"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The type for the shutdown function.
|
||||||
type ShutdownFunction struct {
|
type ShutdownFunction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The shutdown handler for this package.
|
||||||
func (a ShutdownFunction) Shutdown() {
|
func (a ShutdownFunction) Shutdown() {
|
||||||
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down the number generator.`)
|
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 (
|
import (
|
||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
"gopkg.in/mgo.v2"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
correctPassword string = ``
|
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
|
||||||
// This is the name for logging event from this package:
|
genCurrentTime time.Time = time.Now().UTC() // The time for the last generated number
|
||||||
senderName LM.Sender = `System::NumGen::Producer`
|
genCurrentMillisecond int = 0 // The millisecond for the last generated number
|
||||||
isActive bool = false
|
genCurrentMillisecondCounter int = 0 // The counter of how many numbers are generated at the same time
|
||||||
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
|
|
||||||
)
|
)
|
||||||
|
@ -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 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 template engine for web applications
|
||||||
* A distributed half-automated configuration management
|
* A distributed half-automated configuration management
|
||||||
|
* A distributed number generator which produces e.g. customer IDs
|
||||||
* A simple I18N support
|
* A simple I18N support
|
||||||
* A simple database abstraction which MongoDB as database back-end
|
* A simple database abstraction which MongoDB as database back-end
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log"
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
"github.com/SommerEngineering/Ocean/Log/Web"
|
"github.com/SommerEngineering/Ocean/Log/Web"
|
||||||
"github.com/SommerEngineering/Ocean/NumGen"
|
|
||||||
"github.com/SommerEngineering/Ocean/Robots"
|
"github.com/SommerEngineering/Ocean/Robots"
|
||||||
"github.com/SommerEngineering/Ocean/StaticFiles"
|
"github.com/SommerEngineering/Ocean/StaticFiles"
|
||||||
"github.com/SommerEngineering/Ocean/WebContent"
|
"github.com/SommerEngineering/Ocean/WebContent"
|
||||||
@ -30,9 +29,6 @@ func InitHandlers() {
|
|||||||
// Handler for other static files:
|
// Handler for other static files:
|
||||||
Handlers.AddPublicHandler(`/staticFiles/`, StaticFiles.HandlerStaticFiles)
|
Handlers.AddPublicHandler(`/staticFiles/`, StaticFiles.HandlerStaticFiles)
|
||||||
|
|
||||||
// Handler for the number generator:
|
|
||||||
Handlers.AddPublicHandler(`/next/number`, NumGen.HandlerGetNext)
|
|
||||||
|
|
||||||
// Handler for the robots.txt:
|
// Handler for the robots.txt:
|
||||||
Handlers.AddPublicHandler(`/robots.txt`, Robots.HandlerRobots)
|
Handlers.AddPublicHandler(`/robots.txt`, Robots.HandlerRobots)
|
||||||
|
|
||||||
@ -49,9 +45,6 @@ func InitHandlers() {
|
|||||||
// Handler for other static files:
|
// Handler for other static files:
|
||||||
Handlers.AddAdminHandler(`/staticFiles/`, StaticFiles.HandlerStaticFiles)
|
Handlers.AddAdminHandler(`/staticFiles/`, StaticFiles.HandlerStaticFiles)
|
||||||
|
|
||||||
// Handler for the number generator:
|
|
||||||
Handlers.AddAdminHandler(`/next/number`, NumGen.HandlerGetNext)
|
|
||||||
|
|
||||||
// Handler for the ICCC to the private side:
|
// Handler for the ICCC to the private side:
|
||||||
Handlers.AddAdminHandler(`/ICCC`, ICCC.ICCCHandler)
|
Handlers.AddAdminHandler(`/ICCC`, ICCC.ICCCHandler)
|
||||||
|
|
||||||
|
@ -11,6 +11,12 @@ func RandomInteger(max int) (rnd int) {
|
|||||||
return
|
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).
|
// Gets a random UUID (v4).
|
||||||
func RandomGUID() (guidString string) {
|
func RandomGUID() (guidString string) {
|
||||||
guidString = uuid.NewV4().String()
|
guidString = uuid.NewV4().String()
|
||||||
|
Loading…
Reference in New Issue
Block a user