Quartz.NET + SQLite Jobstore throwing JobPersistenceException - c#

When adding or removing a job to/from the scheduler, Quartz sporadically throws a JobPersistenceException (following a preceding SQLiteException).
Things that seem noteworthy:
Quartz.NET 2.01 + System.Data.SQLite 1.0.66 (both latest version at time of writing just noticed that there is a binary package for SQLite 1.0.82 available)
the exception is also thrown if there is currently no job/trigger beeing executed (i am monitoring the Quartz listeners)
the Jobs are manually added from UI context (i require about 10-20 repetitions to cause the error but it seems totally random)
Everything seems to be running fine (multiple jobs, parallel execution, persistance after application restart) as long as i don't touch AddJob()/DeleteJob() After extended testing i am sure its not related to adding/removing jobs. The db locking/access problems are a general problem.
Is there any recommended procedure i am not aware that must be followed when adding/deleting jobs?
Is there anything wrong with my ISchedulerFactory configuration? (see below)
Supplemental
I tried using System.Data.SQLite 1.0.82 which made things worse. I get "SQLite error (5): database is locked" almost constantly as soon as Quartz is executing a Job.
Quartz.NET list System.Data.SQLite 1.0.56 as supported db provider so one might expect problems using a newer version. However, i don't consider going back from 1.0.66 as an option since there were lots of improvements/fixes IIRC.
I took a look at the development trunk of Quartz.NET between the 2.0.1 release revision (624) and current head revision (669). There seem to be no related fixes.
I suspect that its a System.Data.SQLite issue. I stumbled over several posts (concerning different SQLite versions) mentioning that there might be some issues with internal disposing of resources, keeping the DB file locked.
Supplemental 2
For now, I gave up on this. I tried a lot of things, but development has to go on. I switched to another database type (Firebird) which so far seems to work fine with Quartz.
If somebody gets this working i would love to hear about it anyway.
-
Exception details:
Quartz.JobPersistenceException: "Couldn't commit ADO.NET transaction. The database file is locked\r\ndatabase is locked"
Stack
bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.ExecuteInNonManagedTXLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreTX.ExecuteInLock(String lockName, Func`2 txCallback)
bei Quartz.Impl.AdoJobStore.JobStoreSupport.RemoveJob(JobKey jobKey)
bei Quartz.Core.QuartzScheduler.DeleteJob(JobKey jobKey)
bei Quartz.Impl.StdScheduler.DeleteJob(JobKey jobKey)
InnerException SQLiteException: "The database file is locked\r\ndatabase is locked"
Stack
bei System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
bei System.Data.SQLite.SQLiteDataReader.NextResult()
bei System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
bei System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
bei System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
bei System.Data.SQLite.SQLiteTransaction.Commit()
bei Quartz.Impl.AdoJobStore.JobStoreSupport.CommitConnection(ConnectionAndTransactionHolder cth, Boolean openNewTransaction)
The source of the exception is "cth.Transaction.Commit();" in this Quartz.NET method.
/// <summary>
/// Commit the supplied connection.
/// </summary>
/// <param name="cth">The CTH.</param>
/// <param name="openNewTransaction">if set to <c>true</c> opens a new transaction.</param>
/// <throws>JobPersistenceException thrown if a SQLException occurs when the </throws>
protected virtual void CommitConnection(ConnectionAndTransactionHolder cth, bool openNewTransaction)
{
CheckNotZombied(cth);
if (cth.Transaction != null)
{
try
{
IsolationLevel il = cth.Transaction.IsolationLevel;
cth.Transaction.Commit();
if (openNewTransaction)
{
// open new transaction to go with
cth.Transaction = cth.Connection.BeginTransaction(il);
}
}
catch (Exception e)
{
throw new JobPersistenceException("Couldn't commit ADO.NET transaction. " + e.Message, e);
}
}
}
This is how i create the ISchedulerFactory:
public static ISchedulerFactory CreateSQLiteSchedFactory(SQLiteConnection sqlConn, string tablePrefix) {
// db provider hinzufügen
var metaData = new DbMetadata();
metaData.AssemblyName = "System.Data.SQLite,Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139";
metaData.BindByName = true;
metaData.CommandBuilderType = typeof(SQLiteCommandBuilder);
metaData.CommandType = typeof(SQLiteCommand);
metaData.ConnectionType = typeof(SQLiteConnection);
metaData.ExceptionType = typeof(SQLiteException);
metaData.ParameterDbType = typeof(TypeAffinity);
metaData.ParameterDbTypePropertyName = "DbType";
metaData.ParameterNamePrefix = "#";
metaData.ParameterType = typeof(SQLiteParameter);
metaData.UseParameterNamePrefixInParameterCollection = true;
DbProvider.RegisterDbMetadata("SQLite-1066", metaData);
// konfiguration für factory erstellen
NameValueCollection properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "TestScheduler";
properties["quartz.scheduler.instanceId"] = "instance_one";
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "5";
properties["quartz.threadPool.threadPriority"] = "Normal";
properties["quartz.jobStore.misfireThreshold"] = "60000";
properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
properties["quartz.jobStore.useProperties"] = "false";
properties["quartz.jobStore.dataSource"] = "default";
properties["quartz.jobStore.tablePrefix"] = tablePrefix;
properties["quartz.jobStore.clustered"] = "true";
properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
properties["quartz.dataSource.default.connectionString"] = sqlConn.ConnectionString;
properties["quartz.dataSource.default.provider"] = "SQLite-1066";
// factory erzeugen
return new StdSchedulerFactory(properties);
}
The SQLiteConnection is created with connectionstring similar to "Data Source=c:\mydb.db;Version=3;" and all the quartz tables are initialized using the supplied SQL script

You have to set this one to true in the properties:
properties["quartz.jobStore.txIsolationLevelSerializable"] = "true";

The cause of this error is most likely to be because of multiple concurrent writes on the SQLite db, sqlite can accept multiple read-only connections only, but the can't accept simultaneous writes!
http://www.sqlite.org/faq.html#q5

Related

What a problem with this buttom in windows form and sqlconnection

Details:
System.InvalidOperationException
HResult=0x80131509
Message=BeginExecuteNonQuery requires an open and available Connection. The connection's current state is closed.
Source=System.Data.SqlClient
StackTrace:
at System.Data.SqlClient.SqlCommand.ValidateCommand(Boolean async, String method)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName) at System.Data.SqlClient.SqlCommand.BeginExecuteNonQuery(AsyncCallback callback, Object stateObject) at System.Threading.Tasks.TaskFactory1.FromAsyncImpl(Func3 beginMethod, Func2 endFunction, Action1 endAction, Object state, TaskCreationOptions creationOptions) at System.Threading.Tasks.TaskFactory1.FromAsync(Func3 beginMethod, Func2 endMethod, Object state)
at System.Data.SqlClient.SqlCommand.ExecuteNonQueryAsync(CancellationToken cancellationToken)
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Praktika.Form1.<button2_Click>d__9.MoveNext() in D:\Programminng\практика\Praktika\Praktika\Form1.cs:line 149
This exception was originally thrown at this call stack:
[External Code]
Praktika.Form1.button2_Click(object, System.EventArgs) in Form1.cs
private async void button2_Click(object sender, EventArgs e)
{
if (label8.Visible)
label8.Visible = false;
if (!string.IsNullOrEmpty(textBox11.Text) && !string.IsNullOrWhiteSpace(textBox11.Text) &&
!string.IsNullOrEmpty(textBox6.Text) && !string.IsNullOrWhiteSpace(textBox6.Text) &&
!string.IsNullOrEmpty(textBox5.Text) && !string.IsNullOrWhiteSpace(textBox5.Text) &&
!string.IsNullOrEmpty(textBox4.Text) && !string.IsNullOrWhiteSpace(textBox4.Text))
{
SqlCommand command = new SqlCommand("UPDATE [Praktika] SET [LastNAme]=#LastNAme,[Passport]=#Passport,[Cod]=#Cod WHERE [Id]=#Id", sqlConnection);
command.Parameters.AddWithValue("Id", textBox11);
command.Parameters.AddWithValue("LastName", textBox6);
command.Parameters.AddWithValue("Passport", textBox5);
command.Parameters.AddWithValue("Cod", textBox4);
await command.ExecuteNonQueryAsync();
}
else if(!string.IsNullOrEmpty(textBox11.Text) && !string.IsNullOrWhiteSpace(textBox11.Text))
{
label8.Visible = true;
label8.Text = "Id должен быть заполнен!";
}
else
{
label8.Visible = true;
label8.Text = "Поля 'Фамилия','Пасспорт и 'Код' должны быть заполнены";
}
}
[enter image description here][1]
[1]: https://i.stack.imgur.com/F9oyl.png
BeginExecuteNonQuery requires an open and available Connection. The connection's current state is closed
You need to call sqlConnection.Open() before you await command.ExecuteNonQueryAsync();
I don't recommend holding onto a connection at class level; hold a connection string instead and make a new connection:
using var conn = new SqlConnection(classLevelConnStr);
using var command = new SqlCommand(sql, conn);
//parameters etc here
conn.Open();
command.Execute...
conn.Close(); //if you're going to do more work before you return, close after you execute
If you aren't going to do more work after you execute, you can leave the exiting of the using scope to dispose/close the connection. Opening/closing doesn't actually forge a TCP connection to the server, it simply leases and returns a connection from a pool of kept-open TCP connections, for best performance, unless you've turned off pooling (don't).
By holding onto a connection for a long time you can accidentally leave it open (leased), which prevents its re-use, and you can also increase the risk of running into "the connection already has an open reader" type issues if you do something like looping a reader and firing off update queries while you loop; make a new connection every time you want one - it's not a slow thing to create
--
Separately to this:
Please rename your controls after you drop them on a form. It takes seconds to do and prevents your code filling up with meaningless variable names. The process of filling code with junk variable names is called obfuscation, a technique designed to make code hard to read and understand. At some point someone else will have to read your code (us, your coworker or replacement, even you in 6 months time..) and they would rather read firstNameTextBox, ageNumericUpDown than textBox57, numericUpDown13. To understand why it's important, just imagine how confusing C# would be if instead of names like string.Length, string.Substring(int start, int length) Microsoft had called them things like Type1.Integer1, Type1.Method1(Type2 parameter1, Type2 parameter2) to chop up a string you'd be writing code like Type1 s = "Hello"; s = s.Method1(0, s.Integer1 - 3); - it's gobbledegook
To rename a control, you right click on it, choose Properties, and then type a new name in the (Name) line of the property grid - it's at the top. You don't even have to right click if the properties grid is already open, just left click the control
Avoid using AddWithValue on SQLServer. Some databases don't care, but SQLS does: https://www.dbdelta.com/addwithvalue-is-evil/
This code is wonky:
command.Parameters.AddWithValue("LastName", textBox6);
It should look something like:
command.Parameters.Add("#LastName", SqlDbType.VarChar, 30).Value = lastNameTextBox.Text);
Replace 30 with the size of the db column and if your column is an NVarChar change the SqlDbType.VarChar to SqlDbType.NVarChar too
This latter form implements the "rename your controls", it adds a parameter using the exact type on the DB side, and it sets the value to the Text of the textbox.. This latter point is important; the text the user typed is in the .Text property. If you just add a textbox object itself you'll probably end up filling your db up with [System.Windows.Forms.TextBox] in every cell of every row

MSSQL Error 'The underlying provider failed on Open' using WCF & EntityFramework

I use the EntityFramework 6 to make database queries:
public class Entities : DbContext {
public Entities ();
public DbSet<Analysis> Analysis { get; set; }
}
public class TableController {
public function List<DataSet> GetDataSets () {
var query = "SELECT ... FROM ... WHERE ...";
var queryReturnType = typeof(Analysis);
var result = this.Entities.Database.SqlQuery(queryReturnType, query);
foreach (var elem in result) {
...
}
}
}
I use all of this in a self-hosting WCF-service, which allows multi-threading:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class RestService : IRestService {
...
}
The query works fine for 90%-95% of all requests, but sometimes I receive an exception from the EntityFramework at the line where I try to iterate over the results of the query (foreach):
Error with underlying provider with open
at System.Data.Entity.Core.EntityClient.EntityConnection.Open()
at System.Data.Entity.Core.Objects.ObjectContext.EnsureConnection(Boolean shouldMonitorTransactions)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func'1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass65'1.b__63()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func'1 operation)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryReliably[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQuery[TElement](String commandText, ExecutionOptions executionOptions, Object[] parameters)
at System.Data.Entity.Internal.InternalContext.<>c__DisplayClass14'1.b__13()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
The service is hosted on a Windows Server 2012 R2 and when I execute the service on a local Windows 10 machine, there error does not occure.
What may cause the error?
I got the same issue with EF. At beginning, the exception was thrown once a day, after 10 times/day and recently 48 times/day. Of course, we cannot reproduce this "The underlying provider failed on open". I did everything, be sure we don't leak connections, always use
using (var context = new DataContext()) {
List<Billing> result = (from item in context.Billing.AsNoTracking()
select item).ToList();
return result;
}
For us, the solution was to force the opening of the connection.
public DataContext() : base("your connectionstring")
**this.Database.Connection.Open();**
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
Hope it works for you, too!
Looking at your connection string, it looks valid. I found this blog post, the problem here is that they were using Integrated Security. If you are running on IIS, your IIS user needs access to the database.
If you are using Entity Framework with Transactions, Entity Framework automatically opens and closes a connection with each database call. So when using transactions, you are attempting to spread a transaction out over multiple connections. This elevates to MSDTC.
(See this reference for more information.)
Please read this article:
MSSQL Error 'The underlying provider failed on Open'
Entity Framework The underlying provider failed on Open
Error: The underlying provider failed on Open. How to resolve it?

MySQL 6.9.8 - System.String.Substring exception when opening new connection

Every now and then, when I'm making a database query I run into a problem with ExecuteNonQuery in the MySQL.Data library.
The exception which gets raised is this:
Exception thrown: 'System.ArgumentOutOfRangeException' in CommonLanguageRuntimeLibrary ("Length cannot be less than zero.")
There are my logs from IntelliTrace from an Azure Worker Role.
It works most of the time but when this happens it stops the normal processing of the worker.
I'm loading the connection string from app.config, this is what it looks like:
<add key="DatabaseConnectionString" value="Server=localhost; Port=3306; Uid=user; Pwd=mypassword; Pooling=false;" />
I select the database at runtime with every request because it keeps changing which database it's connecting to.
Is there anything I can do to stop this from occurring and allow the new connection to open correctly?
Edit
Upon further investigation and crawling through the MySQL.Data Source code I've drilled down into this getter.
[DisplayName("program_name")]
public string ProgramName
{
get
{
string name = Environment.CommandLine;
try
{
string path = Environment.CommandLine.Substring(0, Environment.CommandLine.IndexOf("\" ")).Trim('"');
name = System.IO.Path.GetFileName(path);
if (Assembly.GetEntryAssembly() != null)
name = Assembly.GetEntryAssembly().ManifestModule.Name;
}
catch (Exception ex)
{
name = string.Empty;
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
return name;
}
}
Now is it possible that the WorkerRole will start returning a different value for the Environment.CommandLine property sometime during the execution? That's what seems to be happening because it works at the beginning and then fails after a while (2-3 days.)

StackExchange.Redis with Azure Redis is unusably slow or throws timeout errors

I'm moving all of my existing Azure In-Role cache use to Redis and decided to use the Azure Redis preview along with the StackExchange.Redis library (https://github.com/StackExchange/StackExchange.Redis). I wrote all the code for it without much problem, but when running it is absolutely unusably slow and constantly throws timeout errors (my timeout period is set to 15 seconds).
Here is the relevant code for how I am setting up the Redis connection and using it for simple operations:
private static ConnectionMultiplexer _cacheService;
private static IDatabase _database;
private static object _lock = new object();
private void Initialize()
{
if (_cacheService == null)
{
lock (_lock)
{
if (_cacheService == null)
{
var options = new ConfigurationOptions();
options.EndPoints.Add("{my url}", 6380);
options.Ssl = true;
options.Password = "my password";
// needed for FLUSHDB command
options.AllowAdmin = true;
// necessary?
options.KeepAlive = 30;
options.ConnectTimeout = 15000;
options.SyncTimeout = 15000;
int database = 0;
_cacheService = ConnectionMultiplexer.Connect(options);
_database = _cacheService.GetDatabase(database);
}
}
}
}
public void Set(string key, object data, TimeSpan? expiry = null)
{
if (_database != null)
{
_database.Set(key, data, expiry: expiry);
}
}
public object Get(string key)
{
if (_database != null)
{
return _database.Get(key);
}
return null;
}
Performing very simple commands like Get and Set often time out or take 5-10 seconds to complete. Seems like it kind of negates the whole purpose of using it as a cache if it's WAY slower than actually fetching the real data from my database :)
Am I doing anything obviously incorrect?
Edit: here are some stats that I pulled from the server (using Redis Desktop Manager) in case that sheds some light on anything.
Server
redis_version:2.8.12
redis_mode:standalone
os:Windows
arch_bits:64
multiplexing_api:winsock_IOCP
gcc_version:0.0.0
process_id:2876
tcp_port:6379
uptime_in_seconds:109909
uptime_in_days:1
hz:10
lru_clock:16072421
config_file:C:\Resources\directory\xxxx.Kernel.localStore\1\redis_2092_port6379.conf
Clients
connected_clients:5
client_longest_output_list:0
client_biggest_input_buf:0
client_total_writes_outstanding:0
client_total_sent_bytes_outstanding:0
blocked_clients:0
Memory
used_memory:4256488
used_memory_human:4.06M
used_memory_rss:67108864
used_memory_rss_human:64.00M
used_memory_peak:5469760
used_memory_peak_human:5.22M
used_memory_lua:33792
mem_fragmentation_ratio:15.77
mem_allocator:dlmalloc-2.8
Persistence
loading:0
rdb_changes_since_last_save:72465
rdb_bgsave_in_progress:0
rdb_last_save_time:1408471440
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
Stats
total_connections_received:25266
total_commands_processed:123389
instantaneous_ops_per_sec:10
bytes_received_per_sec:275
bytes_sent_per_sec:65
bytes_received_per_sec_human:
Edit 2: Here are the extension methods I'm using for Get/Set -- they are very simple methods that just turn an object into JSON and call StringSet.
public static object Get(this IDatabase cache, string key)
{
return DeserializeJson<object>(cache.StringGet(key));
}
public static void Set(this IDatabase cache, string key, object value, TimeSpan? expiry = null)
{
cache.StringSet(key, SerializeJson(value), expiry: expiry);
}
Edit 3: here are a couple example error messages:
A first chance exception of type 'System.TimeoutException' occurred in StackExchange.Redis.dll
Timeout performing GET MyCachedList, inst: 11, queue: 1, qu=1, qs=0, qc=0, wr=0/1, in=0/0
A first chance exception of type 'System.TimeoutException' occurred in StackExchange.Redis.dll
Timeout performing GET MyCachedList, inst: 1, queue: 97, qu=0, qs=97, qc=0, wr=0/0, in=3568/0
Here is the recommended pattern, from the Azure Redis Cache documentation:
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => {
return ConnectionMultiplexer.Connect("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
});
public static ConnectionMultiplexer Connection {
get {
return lazyConnection.Value;
}
}
A few important points:
It uses Lazy<T> to handle thread-safe initialization
It sets "abortConnect=false", which means if the initial connect attempt fails, the ConnectionMultiplexer will silently retry in the background rather than throw an exception.
It does not check the IsConnected property, since ConnectionMultiplexer will automatically retry in the background if the connection is dropped.
I was having similar issues. Redis cache was unusually slow but was definitely caching. In some cases, it took 20-40 seconds to load a page.
I realized that the cache server was in a different location than the site's. I updated the cache server to live in the same location as the website and now everything works as expected.
That same page now loads in 4-6 seconds.
Good luck to anyone else who's having these issues.
The problem is how the connection object created and used. we faced exact problem initially and fixed with a single connection object getting used across all web requests. And we check is it null or connected in session start for graceful re creating object. that fixed the issue.
Note: Also check in which Zone of Azure your Redis Cache instance is
running and Which Zone your Web Server exist. It is better to maintain
both in Same Zone
In Global.ascx.cs file
public static ConnectionMultiplexer RedisConnection;
public static IDatabase RedisCacheDb;
protected void Session_Start(object sender, EventArgs e)
{
if (ConfigurationManager.ConnectionStrings["RedisCache"] != null)
{
if (RedisConnection == null || !RedisConnection.IsConnected)
{
RedisConnection = ConnectionMultiplexer.Connect(ConfigurationManager.ConnectionStrings["RedisCache"].ConnectionString);
}
RedisCacheDb = RedisConnection.GetDatabase();
}
}
It worked in my case. Don't forget to increase the SyncTimeout. The default is 1 second.
private static Lazy<ConnectionMultiplexer> ConnectionMultiplexerItem = new Lazy<ConnectionMultiplexer>(() =>
{
var redisConfig = ConfigurationOptions.Parse("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
redisConfig.SyncTimeout = 3000;
return ConnectionMultiplexer.Connect(redisConfig);
});
Check if you your Azure Redis Cache and the Client in the same region in Azure. For example, you might be getting timeouts when your cache is in East US but the client is in West US and the request doesn't complete in synctimeout time or you might be getting timeouts when you are debugging from your local development machinex.
It’s highly recommended to have the cache and in the client in the same Azure region. If you have a scenario to do a cross region calls, you would want to set the synctimeout to a higher value.
Read more:
https://azure.microsoft.com/en-us/blog/investigating-timeout-exceptions-in-stackexchange-redis-for-azure-redis-cache/
In our case the issue is when using SSL connection. You're showing that your desktop manager is running on the non-SSL port, but your code is using SSL.
A quick benchmark on our Azure redis without SSL, retrieving around 80k values with an LRANGE command (also with .net and StackExchange.Redis) is basically instand. When SSL is used, the same query takes 27 seconds.
WebApp: Standard S2
Redis: Standard 1 GB
Edit: Checking the SLOWLOG, Redis itself seems to actually hit slowlog with it's 14ms time it takes or so to grab the rows, but this is far from the actual transfer with SSL enabled. We ended up with a premium Redis to have some sort of security between Redis and Web Apps.

Entity Framework new transaction is not allowed because there are other threads running in the session, multi thread save

I'm tryng to save on a DB the log of a multi thread processo but I'm getting the following error: new transaction is not allowed because there are other threads running in the session.
in each tread I have this function:
internal bool WriteTrace(IResult result, string message, byte type)
{
SPC_SENDING_TRACE trace = new SPC_SENDING_TRACE(
message,
Parent.currentLine.CD_LINE,
type,
Parent.currentUser.FULLNAME,
Parent.guid);
Context.SPC_SENDING_TRACE.AddObject(trace);
if (Context.SaveChanges(result) == false)
return false;
return true;
}
the Context is different for each thread, but the connection with the DB is always the same.
is there a way to solve this problem?
thank you
Andrea
You should create a context for each transaction and then dispose it, you can do that like this:
using(var ctx = new MyContext()) {
//do transaction here
}
After the closed bracket the context is disposed.
For better understanding refer to this post where you can find a great answer by ken2k. Hope you can fix you issue :)
UPDATE:
You should also try adding .ToList() to every LINQ query you have. When you iterate over a LINQ result, you can't make any changes until the iteration has finished. Check if you have something like that or share more code i.e. the piece of code where you call WriteTrace. Hope that this time this actually helps you.
I use entity framework in a multi-threaded environment, where any thread, ui and background (both STA and MTA), can concurrently update the same database. I resolved this problem by re-creating the entity connection from scratch at the start of usage on any new background thread. Examining the entity connection instance ConnectionString shows a reader guid which I assume is used to link common connection instances. By recreating the entity connection from scratch the guid values are different for each thread and no conflict appears to occur.
// Build the connection string.
var sqlBuilder = new SqlConnectionStringBuilder();
sqlBuilder.DataSource = serverName;
sqlBuilder.InitialCatalog = databaseName;
sqlBuilder.MultipleActiveResultSets = true;
...
var providerString = sqlBuilder.ToString();
var sqlConnection = new SqlConnection(providerString);
// Build the emtity connection.
Assembly metadataAssembly = Assembly.GetExecutingAssembly();
Assembly[] metadataAssemblies = { metadataAssembly };
var metadataBase = #"res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl";
var dbModelMetadata = String.Format(metadataBase, objectContextTypeModelName);
// eg: "res://*/Models.MyDatabaseModel.csdl|res://*/Models.MyDatabaseModel.ssdl|res://*/Models.MyDatabaseModel.msl"
var modelMetadataPaths = modelMetadata.Split('|');
var metadataWorkspace = new MetadataWorkspace(modelMetadataPaths, metadataAssemblies);
var entityDbConnection = new EntityConnection(metadataWorkspace, sqlConnection);
return entityDbConnection;

Categories

Resources