Hi I need to upload enormous amount of small text info into the MySQL.
Unfortunately there no BulkOp with MySQL, what I am trying to use go-routines to parallelize transactions.
The problem that all this concurrency and racing stuff drives me a bit crazy.
And I am not sure if to what I come is any good.
A simplified code looks like this, the huge file is scanned line by line, and lines appends to an slice, when size of slice is 1000
sem := make(chan int, 10) //Transactions pool
sem2 := make(chan int) // auxiliary blocking semaphore
for scanner.Scan() {
line := scanner.Text()
lines = append(lines, line)
if len(lines) > 1000 {
sem <- 1 //keep max 10 transactions
go func(mylines ...lineT) {
// I use variadic, to avoid issue with pointers
// I want to path data by values.
<-sem2 // all lines of slice copied, release the lock
gopher(mylines...) //gopher does the transaction by iterating
//every each line. And here I may use slice
//I think.
<-sem //after transaction done, release the lock
}(lines...)
sem2 <- 1 //this to ensure, that slice will be reset,
//after values are copied to func, otherwise
//lines could be nil before the goroutine fired.
lines = nil //reset slice
}
}
How can I better solve thing.
I know I could have make something to bulk import via MySQL utilities, but this is not possible. I neither can make it like INSERT with many values VALUES ("1", "2), ("3", "4") because it's not properly escaping, and I just get errors.
This way looks a ta wierd, but not as my 1st approach
func gopher2(lines []lineT) {
q := "INSERT INTO main_data(text) VALUES "
var aur []string
var inter []interface{}
for _, l := range lines {
aur = append(aur, "(?)")
inter = append(inter, l)
}
q = q + strings.Join(aur, ", ")
if _, err := db.Exec(q, inter...); err != nil {
log.Println(err)
}
}
Related
I have an app that reads a JSON data string from disk to create a string grid listing stocks (ticker, number held, cost) and then calls a stockbroker API to fill in current price and value. It works but the code below is causing memory leaks but whilst there are many internet posts on how to access data in a JSONArray none I could find discuss how to free allocated memory. Can anybody help ? I use Delphi Berlin 10.1 and Indy 10.6 if it matters. There are 3 pages of data, some stocks are on 2 pages, sTickers is a stringlist populated with each stock ticker (about 10 of them) and there are about 14 total stock holdings on the 3 stringgrids (tabGrids[0..2] which are on a page control.
The problem code is:
JSONArray := TJSONObject.ParseJSONValue(s) as TJSONArray;
if JSONarray = nil then
begin
ShowMessage('An error occurred reading the data file, section = [' +
iniSection[tabpage] + '].');
continue;
end;
for row := 0 to JSONArray.Count - 1 do
begin
s := (JSONArray.Items[row] as TJSONObject).Get('ticker').JSONValue.Value;
SL.Add(s);
end;
CombineStrings(sTickers, SL);
tabGrids[tabpage].RowCount := SL.Count + 2; //set row count
for row := 1 to SL.Count do
begin //add fixed data to each grid row
tabGrids[tabpage].Cells[0, row] := SL[row - 1];
tabGrids[tabpage].Cells[4, row] := (JSONArray.Items[row - 1] as TJSONObject).Get('qty').JSONValue.Value;
tabGrids[tabpage].Cells[6, row] := (JSONArray.Items[row - 1] as TJSONObject).Get('cost').JSONValue.Value;
tabGrids[tabpage].Cells[1, row] := (JSONArray.Items[row - 1] as TJSONObject).Get('name').JSONValue.Value;
if not tryStrToFloat(tabGrids[tabpage].Cells[4, row], qty) then qty := 0;
if not tryStrToFloat(tabGrids[tabpage].Cells[6, row], price) then price := 0;
tabGrids[tabpage].Cells[6, row] := FloatToStr(qty*price/100);
end;
tabGrids[tabpage].Width := tabGrids[tabpage].ColWidths[0] +
tabGrids[tabpage].ColWidths[1] + tabGrids[tabpage].ColWidths[2] +
tabGrids[tabpage].ColWidths[3] + tabGrids[tabpage].ColWidths[4] +
tabGrids[tabpage].ColWidths[5] + tabGrids[tabpage].ColWidths[6] + 18;
SL.Clear;
end;
JSONArray.Free;
I assume the (JSONArray.Items[row] as TJSONObject).Get('ticker').JSONValue.Value lines are allocating memory that I am not releasing but I do not see how to release it. Or maybe there is a better way to get the data.
You mention 3 pages of data. You are leaking 2 x TJasonArray, and you are freeing one TJasonArray on the last line right after an end; that doesn't have a corresponding begin.
From that I draw the conclusion that you have a loop (that you did not show) that you run three times. On each time you create a TJasonArray but you free only one, after the loop end;, Move the JSonArray.Free; to before the end;.
To do it simpler, you can just use a TJSONValue to parse and free it at end, without free array :
var
Value: TJsonValue
// ...
begin
Value := TJSONObject.ParseJSONValue(s);
try
Ar := Value.FindValue('item') as TJSONArray;
finally
FreeAndNil(Value);
end;
end;
Instead of :
tabGrids[tabpage].Cells[1, row] := (JSONArray.Items[row - 1] as TJSONObject).Get('name').JSONValue.Value;
You can do
tabGrids[tabpage].Cells[1, row] := JSONArray.Items[row - 1].GetValue<string>('name');
Then, you have a end; before you free the array, if it's a upper loop you will have a memory leaks. Use try finally block to free it and the TStringList.
I am taking over a project with code from another person. I have a PLC that currently has inputs in from pressure sensors and thermocouples. It then scales that data to PSI and temperature in fahrenheit. The way the data is set up from each of those sensors is to be formatted into an array. So, once the data is scaled it is in an array that is also in the Network Variable List of the program. I am trying to take each of these values from the array, record the value every certain amount of time (say 1 recording per second for sake of clarity), and then export each piece of data to a CSV file for every second. Not sure where to even go with this. This is the code I was left with, but I feel as if it it unnecessarily complicated?
//This is the support class for File_Handler
FUNCTION_BLOCK fileWrite
VAR_INPUT
xWrite : BOOL;
sData : STRING(200);
uiLineLength : INT := 200;
sDirectory : STRING := 'C:\ProgramData\CODESYS\CODESYSHMIWinV3\D5050FE1\PlcLogic\data';
//sDirectory : STRING := '/home/cds-apps/PlcLogic/data/';
sFilename : STRING;
END_VAR
VAR_OUTPUT
BytesWritten : __XWORD;
BytesWrittenTotal: DWORD;
xDone: BOOL;
END_VAR
VAR
hFile_: sysfile.RTS_IEC_HANDLE := sysfile.RTS_INVALID_HANDLE;
FileWriteResult: sysfile.RTS_IEC_RESULT;
FileOpenResult: sysfile.RTS_IEC_RESULT;
state: INT;
sys_Us_start: SYSTIME;
sys_Us_end: SYSTIME;
WriteTimeMS: ULINT;
END_VAR
sFilename := CONCAT(sDirectory, sFilename);
hFile_ := SysFileOpen(szFile:= sFilename, am:= ACCESS_MODE.AM_APPEND_PLUS, pResult:= ADR(FileOpenResult));
SysTimeGetUs(pUsTime:=sys_Us_start );
BytesWritten := SysFileWrite(hFile:= hfile_, pbyBuffer:= ADR(sData), ulSize:= uiLineLength, pResult:= ADR(FileWriteResult));
BytesWrittenTotal := BytesWrittenTotal + BytesWritten;
SysTimeGetUs(pUsTime:=sys_Us_end );
WriteTimeMS := (sys_Us_end - sys_Us_start)/1000;
SysFileClose(hFile:= hFile_);
I am not sure where to go with this code. It does create a CSV file, but I was looking to be able to create a CSV file for a piece of data every second? If anyone has any thoughts or resources I could check out that would be great.
A basic example of how to call this routine every second could be the following:
1)
You create a FuncBlock that takes care of calling your logger block.
Let's say you call it LoggerTask.
FUNCTION_BLOCK LoggerTask
VAR_INPUT
sData : STRING(200);
sFilename : STRING;
xExecute : BOOL;
END_VAR
VAR
fbRepeatTask : TON;
fbFileWrite : FileWrite;
uiStep : UINT;
END_VAR
2)
After that create a simple step chain:
(You can obviously extend and customize it as you like, you should add error handling in the case when FileWrite fails to write to file or writes less than expected for example.)
Implementation part:
fbRepeatTask(PT:=T#1S);
fbFileWrite(sData := sData, sFileName := sFileName);
IF xExecute
AND uiStep = 0
THEN
uiStep := 10;
ELSIF NOT xExecute
THEN
uiStep := 0;
fbFileWrite.xWrite := FALSE;
fbRepeatTask.IN := FALSE;
END_IF
CASE uiStep OF
10:
fbFileWrite.xWrite := TRUE;
IF fbFileWrite.xDone
THEN
fbFileWrite.xWrite := FALSE;
uiStep := 20;
END_IF
20:
fbRepeatTask.IN := TRUE;
IF fbRepeatTask.Q
THEN
fbRepeatTask.IN := FALSE;
uiStep := 10;
END_IF
END_CASE
3)
As you can see this block gets executed as soon as xExecute is set to true.
In order to reset the step chain set xExecute to false.
Just run this block cyclically for example like this fbLoggerTask(xExecute := TRUE);
I don't think you posted all the code of your FileWrite block because xDone is not set and xWrite is not checked anywhere.
So make sure that xDone is set to true for one cycle after the String is written to the file (if it's not already been implemented).
I am trying to evaluate the following integral:
where the issue lies with variables like F since it is defined as
F[x_, y_] := f[x, y]/(2*Cto[Norm[x]]*Cto[Norm[y]]) and Cto[x_] := C_t[[Round[x]]]
where C_t is a 1d array and x and y are two vector and I need to access the element of C_t corresponding to the integer of the magnitude of x for example. However, this gives me the following errors when evaluating the integral:
Ci = Flatten[Import["Downloads/ctjulien.txt", "table"]]
Cp = Flatten[Import["Downloads/clphiphi.txt", "table"]]
Subscript[C, t] = Flatten[Import["Downloads/ctobs.txt", "table"]]
Lp[a_] := 1052*{Cos[a], Sin[a]}
vL[L_] := {L, 0}
l[l1_, \[CapitalPhi]1_] :=
l1*{Cos[\[CapitalPhi]1], Sin[\[CapitalPhi]1]}
Cii[x_] := Ci[[Round[x]]]
f[x_, y_] := Cii[Norm[x]]*Dot[x + y, x] + Cii[Norm[y]]*Dot[x + y, y]
Cto[x_] := Subscript[C, t][[Round[x]]]
F[x_, y_] := f[x, y]/(2*Cto[Norm[x]]*Cto[Norm[y]])
Cpp[x_] := Cp[[Round[x]]]
NIntegrate[l1*F[l[l1,\[CapitalPhi]],{L,0}-l[l1,\[CapitalPhi]]]*F[Lp[\[CapitalPhi]p],{L,0}-Lp[\[CapitalPhi]p]]*(Dot[Lp[\[CapitalPhi]p],Lp[\[CapitalPhi]p]-l[l1,\[CapitalPhi]]]*If[Norm[Lp[\[CapitalPhi]p]-l[l1,\[CapitalPhi]]]<=2900,Cpp[Norm[Lp[\[CapitalPhi]p]-l[l1,\[CapitalPhi]]]],0]*f[-{L,0}+l[l1,\[CapitalPhi]],{L,0}-Lp[\[CapitalPhi]p]]+Dot[Lp[\[CapitalPhi]p],Lp[\[CapitalPhi]p]-l[{L,0}-l[l1,\[CapitalPhi]],\[CapitalPhi]]]*If[Norm[Lp[\[CapitalPhi]p]-l[{L,0}-l[l1,\[CapitalPhi]],\[CapitalPhi]]]<=2900,Cpp[Norm[Lp[\[CapitalPhi]p]-l[{L,0}-l[l1,\[CapitalPhi]],\[CapitalPhi]]]],0]*f[-l[l1,\[CapitalPhi]],{L,0}-Lp[\[CapitalPhi]p]]),{\[CapitalPhi],-Pi,Pi},{\[CapitalPhi]p,-Pi,Pi},{l1,2,3000}]
This isn't anywhere near an answer yet, but it is a start. Watch out for Subscript and greek characters and use those appropriately when you test this.
If you insert in front of your code
Ci =Table[RandomInteger[{1,10}],{3000}];
Cp =Table[RandomInteger[{1,10}],{3000}];
Ct =Table[RandomInteger[{1,10}],{3000}];
then you can try to test your code without having your data files present.
If you then test your code you get a stream of "The expression Round[Abs[2+L]] cannot be used as a part" but if you instead insert in front of your code L=2 or some other integer assignment then that error goes away
If you use NIntegrate[yourlongexpression...] then you get a stream of "Round[Sqrt[Abs[l1 Cos[phi]]^2+Abs[l1 Sin[phi]]^2 cannot be used as a part" If you instead use fun[phi_?NumericQ, phip_?NumericQ, l1_?NumericQ]:=yourlongexpression;
NIntegrate[fun[phi,phip,l1]...] then that error goes away.
If you use Table[fun[phi,phip,l1],{phi,-Pi,Pi,Pi/2},{phip,-Pi,Pi,Pi/2},{l1,2,10}] instead of your integral and you look carefully at the output then you should see the word List appearing a number of times. That means you have somelist[[0]] somewhere in your code and Mathematica subscripts all start with 1, not with 0 and that has to be tracked down and fixed.
That is probably the first three or four levels of errors that need to found and fixed.
I've been testing Go in hopes to use it for a new site and wanted to make sure it was as fast or faster than PHP. So I ran a basic test doing bulk inserts in Go and PHP because I'll need bulk inserts.
My tests used transactions, prepared statements, the same machine, the exact same table definition, no index but the PK, and the same logic in the function.
Results:
100k Inserts in PHP (mysqli) was 4.42 seconds
100k Inserts in Go (Go-MySQL-Driver) was 9.2 seconds
The go mysql driver i'm using is the most popular one 'Go-MySQL-Driver' found here: https://github.com/go-sql-driver/mysql
I'm wondering if anyone can tell me if my code in go is not set up right or if this is just how go is.
The functions add a bit of variability to a few of the row variables just so every row isnt the same.
Go Function:
func fill_table(w http.ResponseWriter, r *http.Request, result_string *string, num_entries_to_add int) {
defer recover_show_error(result_string)
db := getDBConn()
defer db.Close()
var int_a int = 9
var int_b int = 4
var int_01 int = 1
var int_02 int = 1451628000 // Date Entered (2016-1-1, 1am)
var int_03 int = 11
var int_04 int = 0
var int_05 int = 0
var float_01 float32 = 90.0 // Value
var float_02 float32 = 0
var float_03 float32 = 0
var text_01 string = ""
var text_02 string = ""
var text_03 string = ""
start_time := time.Now()
tx, err := db.Begin()
if err != nil {
panic(err)
}
stmt, err := tx.Prepare("INSERT INTO " + TABLE_NAME +
"(`int_a`,`int_b`,`int_01`,`int_02`,`int_03`,`int_04`,`int_05`,`float_01`,`float_02`,`float_03`,`text_01`,`text_02`,`text_03`) " +
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)")
if err != nil {
panic(err)
}
defer stmt.Close()
var flip int = 0
for i := 0; i < num_entries_to_add; i++ {
flip = ((int)(i / 500)) % 2
if flip == 0 {
float_01 += .1 // add to Value
} else {
float_01 -= .1 // sub from Value
}
int_02 += 1 // add a second to date.
_, err = stmt.Exec(int_a, int_b, int_01, int_02, int_03, int_04, int_05, float_01, float_02, float_03, text_01, text_02, text_03)
if err != nil {
panic(err)
}
}
err = tx.Commit()
if err != nil {
panic(err)
}
elapsed := time.Since(start_time)
*result_string += fmt.Sprintf("Fill Table Time = %s</br>\n", elapsed)
}
PHP Function:
function FillTable($num_entries_to_add){
$mysqli= new mysqli("localhost", $GLOBALS['db_username'], $GLOBALS['db_userpass'], $GLOBALS['database_name']);
if ($mysqli->connect_errno == 0) {
$int_a = 9;
$int_b = 4;
$int_01 = 1;
$int_02 = 1451628000; // Date Entered (2016-1-1, 1am)
$int_03 = 11;
$int_04 = 0;
$int_05 = 0;
$float_01 = 90.0; // Value
$float_02 = 0;
$float_03 = 0;
$text_01 = "";
$text_02 = "";
$text_03 = "";
$mysqli->autocommit(FALSE); // This Starts Transaction mode. It will end when you use mysqli->commit();
$sql = "INSERT INTO " . $GLOBALS['table_name'] .
"(`int_a`,`int_b`,`int_01`,`int_02`,`int_03`,`int_04`,`int_05`,`float_01`,`float_02`,`float_03`,`text_01`,`text_02`,`text_03`) " .
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)";
$start_time = microtime(true);
if($stmt = $mysqli->prepare($sql)) {
$stmt->bind_param('iiiiiiidddsss', $int_a, $int_b, $int_01, $int_02, $int_03, $int_04, $int_05, $float_01, $float_02, $float_03, $text_01, $text_02, $text_03);
$flip = 0;
for ($i = 1; $i <= $num_entries_to_add; $i++) {
$flip = ((int)($i / 500)) % 2;
if ($flip == 0) {
$float_01 += .1; // add Value
}
else {
$float_01 -= .1; // sub Value
}
$int_02 += 1; // add a second to date.
$stmt->execute(); //Executes a prepared Update
}
$mysqli->commit(); // Transaction mode ends now
$stmt->close(); //Close statement
}
$execute_time = microtime(true) - $start_time;
echo $GLOBALS['html_newline'] . $GLOBALS['html_newline'] .
'FillDataEntryTable Speed: '.$execute_time.' sec' . $GLOBALS['html_newline'] . $GLOBALS['html_newline'];
$thread_id = $mysqli->thread_id; // Get MySQL thread ID
$mysqli->kill($thread_id); // Kill MySQL Server connection
$mysqli->close(); // Close MySQL Server connection
}
}
In my testing to find what language I want to use for my new website I experimented with php, golang, and java. I don't have much experience with any of the languages so anything I say here could be corrected by someone in the future.
My main test was batch inserts into the mysql database because I'll be needing it for an app.
I wanted to move away from php because it's a non-compiled old scripting language which is slower at many things than golang and java. It's also an awkward syntax for many things. However php mysqli is actually 2x faster than golang for large "transactions" unless you awkwardly spawn many go-routines to divide the work up.
During my testing and research I found out a few things.
The PHP mysqli "transactions" api are probably using some kind of batch operations to get a "transaction" done because mysqli has no separate batch functions and the transactions are quicker than single inserts. But in most other languages transactions don't auto-batch everything and don't even increase the execution time. They are just a mechanism to roll back everything in the transaction if something goes wrong. What increases execution time in other languages is using batches.
But one of the big problems with go mysql interface right now appears to be no real support for batch operations. The closest I got was to jerry rig one and make my own batch operation as pointed out by this post (golang - mysql Insert multiple data at once?). Doing this I was able to get the execution time in go from 9.2s to 3.9s without spawning other go routines. But since there's no real support for it the batch operation only returns a single result set for the first operation of the batch. This is worthless to me because I need to return autoinc Ids for my inserted rows. There were other problems with this setup too that I wont go into.
So lastly I tried java on a tomcat server. Tomcat/java installation is a bit more involved than go but programming in java was so much easier and natural. JDBC is an excellent driver with fulls support for easy batch operations with prepared statements. It did 100k inserts in only 1 sec. It's the clear winner in my book. Plus java syntax is much more natural than golang IMO.
I made a random room program with 4 rooms. I'm trying to take the attributes in each room and pass them through to other functions. In each room is a person with a name and an age attribute. I'm trying to pass on those attributes to test against if statements to put out an additional response. How do I pass these values on?
//the random maze room game
package main
//All imports can be combined into ()
import ("fmt"
//Import needed for random operation
"math/rand"
//Import requied to call upon current time
"time"
)
type Person struct {
Name string
Age int
}
func main(){
// These two lines are designed to reset the random Seed every time the program is run
// Unless the randomizer is seeded, Golang 1.6 is pseudo-random,
// always defaulting to Seed(1)
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
switch r1.Intn(4){
case 0:
westRoom()
case 1:
eastRoom()
case 2:
northRoom()
case 3:
southRoom()
default:
lostRoom()
}
p := Person{}
p.Name = Avatarname
p.Age = Avatarage
avatar(&p)
appearance(&p)
}
func westRoom(){
fmt.Println("You find yourself in a room with three walls, and a door behind you.")
fmt.Println("The opposite wall is a window, overlooking the sea")
Avatarname := "Bill"
Avatarage := 25
return
}
func eastRoom(){
fmt.Println("You find yourself in a room with a door on the walls to your left, right, and behind you")
fmt.Println("on the wall across from you is a painting of a mountain scene")
Avatarname := "Mary"
Avatarage := 33
return
}
func northRoom(){
fmt.Println("You find yourself in a room with a door on the wall behind you")
fmt.Println("You see several statues of people standing around the room")
Avatarname := "Joe"
Avatarage := 58
return
}
func southRoom(){
fmt.Println("You find yourself in a room with a door on the wall in front and behind you")
Avatarname := "Abagail"
Avatarage := 67
return
}
func lostRoom(){
fmt.Println("You are unable to find a room in a maze filled only with rooms")
fmt.Println("It's almost like the programmer didn't know what he was doing")
}
func avatar(p.Name, p.Age){
if p.Name == "Bill" || "Joe" {
fmt.Println("You see a man standing in the middle of the room")
} else {
fmt.Println("You see a woman standing in the middle of the room")
}
}
func appeareance(p.Name, p.Age) {
if p.Age > 50 {
fmt.Println("They look old")
} else {
fmt.Println("They look young")
}
}
One way to do this is to change your room functions to return either a Person struct or the attributes of the Person struct.
Check out the live code here: The Go Playground
For example,
func northRoom() *Person{
fmt.Println("You find yourself in a room with a door on the wall behind you")
fmt.Println("You see several statues of people standing around the room")
return &Person{"Joe", 58}
}
Now the function calls in your switch statement can expect a Person struct being returned.
var p *Person
switch r1.Intn(4){
case 0:
p = westRoom()
case 1:
p = eastRoom()
case 2:
p = northRoom()
case 3:
p = southRoom()
default:
p = lostRoom()
}
avatar(p)
appearance(p)
Note that you will need to change the signature of your avatar() and appearance() functions to accept a *Person parameter.
Why not have your room functions return a Person? Example:
func main() {
// These two lines are designed to reset the random Seed every time the program is run
// Unless the randomizer is seeded, Golang 1.6 is pseudo-random,
// always defaulting to Seed(1)
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
var p Person
switch r1.Intn(4) {
case 0:
p = westRoom()
case 1:
p = eastRoom()
case 2:
p = northRoom()
case 3:
p = southRoom()
default:
lostRoom()
}
p.printAvatar()
p.printAppeareance()
}
func westRoom() Person {
fmt.Println("You find yourself in a room with three walls, and a door behind you.")
fmt.Println("The opposite wall is a window, overlooking the sea")
return Person{
Name: "Bill",
Age: 25,
}
}
https://play.golang.org/p/9_PuPsRdXg