I have a lua script that needs to call a zunionstore on a variable number of keys.
I'm trying to execute the following code:
local args = redis.call("zrange", "weight", 0, -1, "WITHSCORES")
local r,w
local count = 0
local cmd = ' '
for i=1,#args,2 do
cmd = cmd .. args[i] .. ":weight " -- building up a list of zsets
count = count + 1
end
redis.call("zunionstore", "p2_test_set", count, cmd)
The important lines being:
cmd = cmd .. args[i] .. ":weight "
Which builds the list of keys, and the actual call:
redis.call("zunionstore", "p2_test_set", count, cmd)
However, when executing I get the following error:
redis-cli EVAL "$(cat p2.lua)" 0
(error) ERR Error running script (call to f_6dc6501103ea64a02798af1cc9132f8337cdcad4): #user_script:9: ERR syntax error
So, how do I pass a variable number of keys, computed within the lua script, to the redis.call("zunionstore"...) command?
Thanks in advance!
The way I solved this is:
for i=0,#array,1 do
local tmp = {'zunionstore', key, #array[i], unpack(array[i])}
redis.call(unpack(tmp))
end
Related
I am getting this error:
System.Data.SqlServerCe.HashSafeHandle" could not release handle with value 0x08D7E430
suddenly using
System.Data.SqlServer_Ce_unofficial
and then calling this simple function (which worked all the time):
Using nCmdSel As SqlServerCe.SqlCeCommand = uCn.CreateCommand
With nCmdSel
.CommandText = uCommandText
End With
Dim r As SqlServerCe.SqlCeDataReader = nCmdSel.ExecuteReader
Return r
End Using
Does anybody have any idea why?
Background
I'm maintaining a VB.Net (formerly VB6) utility that exports records to a flat file. One of our customers reported that the new version was taking a long time to run, and digging into the trace it was easy to see why: (I've obfuscated this slightly)
exec [sys].sp_describe_first_result_set
N'Update [MAIN].[dbo].[Inventory] Set ExportId = #P1 Where Comp = #P2 and Osite = #P3 and Key = #P4',
N'#P1 numeric(10),#P2 varchar(6),#P3 varchar(6),#P4 int',1
This and statements like it were taking half a second each. The utility has to update the main inventory table with some information about the export, and the table is heavily triggered, so sp_describe_first_result_set has to simulate all the triggers in order to determine the result - I can verify that later down in the trace.
Problem
I can't figure out why exactly my code is calling sp_describe_first_result_set for an update statement, a thing that doesn't even have a result set. The command setup doesn't look like it's doing anything weird:
Dim connection = New ADODB.Connection
connection.ConnectionString = config.AdoConnectionString
connection.CursorLocation = CursorLocationEnum.adUseClient
connection.Open()
cmd = New ADODB.Command
With cmd
.ActiveConnection = connection
.CommandType = CommandTypeEnum.adCmdText
.CommandText = "Update [MAIN].[dbo].[Inventory] " &
"Set ExportId = ? " &
"Where Comp = ? and Osite = ? and Key = ?"
.Parameters.Append(NumericParameter(cmd.CreateParameter("ExportId", DataTypeEnum.adNumeric, ParameterDirectionEnum.adParamInput), 10, 0))
.Parameters.Append(cmd.CreateParameter("Comp", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 6))
.Parameters.Append(cmd.CreateParameter("Osite", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 6))
.Parameters.Append(cmd.CreateParameter("Key", DataTypeEnum.adInteger, ParameterDirectionEnum.adParamInput))
End With
cmd.Parameters("ExportID").Value = lExportId
cmd.Parameters("Comp").Value = sComp
cmd.Parameters("Osite").Value = sOsite
cmd.Parameters("Key").Value = iKey
cmd.Execute()
Is there some setting I'm just missing that's making it run sp_describe_first_result_set all the time? Is there a way to stop it from doing that?
You need to explicitly say you don't want a recordset.
In VB6, an ADODB Command object can tell whether or not it should return a recordset based on whether the value of the Execute() function is being assigned to anything.
In VB.NET, this is no longer done, but execution options flags can be provided as arguments to the Execute() function. Calling the command like this:
cmd.Parameters("ExportID").Value = lExportId
cmd.Parameters("Comp").Value = sComp
cmd.Parameters("Osite").Value = sOsite
cmd.Parameters("Key").Value = iKey
cmd.Execute(Options:=ExecuteOptionEnum.adExecuteNoRecords)
will not call sp_describe_first_result_set to establish the parameters of the record set, but will only execute the command directly, the same way more modern methods such as SqlCommand.ExecuteNonQuery() will.
Out of curiosity, does the modern equivalent to that code also trace as performing this op?
Dim cmd as New SqlCommand( _
"Update [MAIN].[dbo].[Inventory] Set ExportId = #e Where Comp = #c and Osite = #o and Key = #k",
"Data Source=YOUR_SERVER;Initial Catalog=YOUR_DB;User ID=YOUR_USER_EG_sa;Password=YOUR_PASSWORD" _
)
cmd.Connection.Open()
cmd.Parameters.AddWithValue("#e", lExportId)
cmd.Parameters.AddWithValue("#c", sComp)
cmd.Parameters.AddWithValue("#o", sOsite)
cmd.Parameters.AddWithValue("#k", iKey)
cmd.ExecuteNonQuery()
I use AddWithValue for testing/convenience purposes here, but you should probably avoid it in prod because it can cause performance issues with SQLS - see that link for advice on how to craft parameters properly
I have issues with sorting an array of values. It does not matter what type of array mechanism I use, I always get errors like "Object reference not set to an instance of an object" while using Tuple, or "Value cannot be null" while sorting Dictionary.
The interesting thing is that the errors I mentioned above occur only while I use the parallel loop to process data faster. When I don't use a parallel loop, the data is sorted without any errors.
Here is a line that sorts Dictionary:
DictionaryValues = DictionaryValues.OrderBy(Function(x) x.Value).ToDictionary(Function(x) x.Key, Function(x) x.Value)
I also tried Tuple for this, and here is a Tuple sorting line:
lstToSort = lstToSort.OrderBy(Function(i) i.Item2).ToList
Finally, here is my "Parallel.For" block of code:
Dim ProcessedCTDataValues As New Dictionary(Of String, Double)
Dim t As Task = Task.Run(Sub()
Parallel.For(0, dv.Count - 1,
Sub(iii As Integer)
Dim cmprName As String = dv(iii)(0).ToString & "; " & dv(iii)(1).ToString & "; " & dv(iii)(2).ToString & "; " & dv(iii)(3).ToString
Dim cmprAddress As String = dv(iii)(2).ToString
If Not ProcessedCTDataValues.ContainsKey(cmprName) Then
Dim similarityName As Double = 0
Dim similarityAddress As Double = 0
similarityName += GetSimilarity.Invoke(InputKeyword.ToLower, cmprName.ToLower)
similarityAddress += GetSimilarity.Invoke(StreetNumber.Invoke.ToLower, cmprAddress.ToLower)
If cmprName.ToLower.Contains(InputKeyword.ToLower) Then similarityName += 1
If InputKeyword.ToLower.Contains(cmprName.ToLower) Then similarityName += 1
For Each word As String In InputKeyword.Split({" "c}, StringSplitOptions.RemoveEmptyEntries)
If cmprName.ToLower.Contains(word.ToLower) Then similarityName += 0.3
Next
ProcessedCTDataValues.Add(cmprName, similarityName + similarityAddress)
End If
End Sub)
End Sub)
t.Wait()
Again, if I don't use a parallel loop, I can sort data without any issues. But if I use this loop, I always get an error. Any help would be greatly appreciated! Thanks!
To answer my own question - this is a very specific problem, which can be solved by using a Concurrent dictionary.
From Microsoft Docs: "Represents a thread-safe collection of key/value pairs that can be accessed by multiple threads concurrently."
Since I am using Parallel, this was a perfect solution to look forward to.
Instead of using Dictionary to add keys and values while using "Parallel.For", you can create a Concurrent dictionary, add items to it in the same way you would add to a regular dictionary, and use it in the same way as well.
To declare a Concurrent dictionary, you can use:
Dim concDict As ConcurrentDictionary(Of String, Double) = New ConcurrentDictionary(Of String, Double)()
To add items to it, use:
concDict.GetOrAdd(cmprName, similarityName + similarityAddress)
The concurrent dictionary is part of the System.Collections.Concurrent Namespace
Cheers!
I am running a loop that evaluates input given by the user over more than 150k objects. The user sets the info to read like "obj.Name", "obj.Path", "obj.Date"... which may contain also logic evaluations such as "IIf(obj.Params>5,1,0)". It is then provided into the programm as a string.
I used the Evaluate() functions and it does work well, just that it is slow. It takes almost 6h to go over all elements. I was thinking if there is a way in which I can take the requested info and turn it into a straight-forward executable somehow, and avoid using the Evaluate expression in the whole loop (it runs for the number of requested data by the user * 150k).
This is a schematic of the loop I am running:
For Each Object in ObjectsList
For Each UserRequest in Requests
ResultsMatrix(i,j) = Evaluate(Requests(i))
j += 1
Next
i += 1
Next
I store then the results in a Matrix which is pasted in an Excel file at the end. Is there a way in which I can do sort of working the string to be evaluated into a function's return? I'd like to avoid usig the Eval function and parse directly the string in an executable and dont evaluate it for each object. Any tips on speeding up the loop?
It might be worth considering writing the requests into a set of functions and using the .NET CodeDom compilers to build it into a DLL. You can then load the assembly, find the right functions using reflection and put them into an array, then call them using reflection - that way you'll be calling .NET code and it should be far faster. Some (incomplete) code to get you started from a project where I have done this...
Private Function CombineCode() As String
Dim ret As New System.Text.StringBuilder
ret.AppendLine("Imports System")
ret.AppendLine("Imports Microsoft.VisualBasic")
ret.AppendLine()
ret.AppendLine("Namespace " & MainNamespace)
ret.AppendLine("Public Class " & MainClassName)
For Each e In _Entries
ret.AppendLine(e.Value.Code)
Next
ret.AppendLine("End Class")
ret.AppendLine("End Namespace")
Return ret.ToString
End Function
Private Function Compile(Code As String) As Assembly
'Dim d As New Dictionary(Of String, String)
'd.Add("langversion", "14")
Dim VBP As New Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider()
Dim PM As New System.CodeDom.Compiler.CompilerParameters
'PM.GenerateInMemory = True
PM.GenerateExecutable = False
PM.OutputAssembly = IO.Path.Combine(_Path, GenerateFileName() & ".dll") ' "Generated.dll"
PM.MainClass = MainClassName
PM.IncludeDebugInformation = True
Dim ASM As System.Reflection.Assembly
For Each ASM In AppDomain.CurrentDomain.GetAssemblies()
Try
If ASM.Location <> "" Then PM.ReferencedAssemblies.Add(ASM.Location)
Catch
End Try
Next
PM.ReferencedAssemblies.Add("System.Web.dll")
'Get compilation results
Dim Results As System.CodeDom.Compiler.CompilerResults
Results = VBP.CompileAssemblyFromSource(PM, Code)
'Show possible compilation errors
Dim Err As System.CodeDom.Compiler.CompilerError
For Each Err In Results.Errors
Throw New SyntaxErrorException("Error N. " & Err.ErrorNumber &
" Message: " & Err.ErrorText & " Line " & Err.Line & " in code " & vbCrLf & Code)
Next
Return Results.CompiledAssembly
End Function
Private Sub FindMethods()
Dim dt = (From t In _LatestAssembly.GetTypes() Where t.Name = MainClassName).Single
For Each e In _Entries.Values
e.Method = dt.GetMethod(e.MethodName)
Next
End Sub
Assembly = Assembly.LoadFrom(System.IO.Path.Combine(Path, sd.LatestAssemblyFile))
The Evaluate function is just resources on the computer itself. It's a great candidate for using Parallel.For.
In this case, j is the implied index.
For Each Object in ObjectsList
Parallel.For(0, Requests.Length, New ParallelOptions(), Sub(j, loopState)
ResultsMatrix(i,j) = Evaluate(Requests(j))
End Sub
)
i += 1
Next
Note, that Requests(i) is getting called repeatedly and produces the same result, so I assume you mean Requests(j).
Im using getPrivateProfileString to read from an API. Here I call the method and get a store number
Rec = String(255, 0)
NC = GetPrivateProfileString("ConfigInfo", "store", "", Rec, 255, "C:\MyPath")
Now when I debug Rec has no value
However when I use Rec in a message box it shows its value as expected.
Why is the variable Rec an empty variable instead of holding my store number as expected? Why does Rec's value seem to be as expected in a message box?