I create the connection to the server in the main thread of application:
ClassA::ClassA(void)
{
mpMySQL = mysql_init(NULL);
if (!mysql_thread_safe())
{
// Error;
}
}
Later there is a connection to the server:
void ClassA::OpenConnection(/*inParams*/)
{
.....
mysql_real_connect(mpMySQL, /*inParams*/);
.....
}
Now I use mpMySQL to execute queries:
MyRes ClassA::SQLQuery(MyString inQuery)
{
...
if (!mysql_real_query(mpMySQL, inQuery, inQuery.length()))
{
...
}
}
While the app uses the object of ClassA in the main thread - all works fine.
But if the method
A->SQLQuery()
invokes from the thread - the application crashes on execution
mysql_real_query()
======
As I understand this happens because the mpMySQL was initialized in one thread and was used in another. Am I right?
Is the way to do this trick? Or I should initialize MYSQL structure in every separated thread (and make separate connection) to execute queries?
mysql_thread_init() is the trick. You need to execute it for every thread that is using C API.
mysql_init() does it implicitly.
Related
I'm implementing Queue triggered azure function - I'm using a Mediator Pattern library called Mediatr for enhancing command query segregation - and using the latest run-time (2.0.12382.0) constructor dependency injection in Azure Function according to the following tutorial
https://devkimchi.com/2019/02/22/performing-constructor-injections-on-azure-functions-v2/
For each Azure function trigger, I call a Mediatr CommandHandler but i'm receiving error :
"A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe. This could also be caused by a nested query being evaluated on the client, if this is the case rewrite the query avoiding nested invocations."
The error states that i'm trying to access the same instance of DbContext from parallel tasks. however I only have one command handler (Mediatr Handler) and one Query Handler. and i'm using constructor injection for that
I tried to change the Meditr service to be transient in the startup , but still receive the same error on testing the function inside the azure function emulator
Startup Class
public class StartUp : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var connection = configuration.GetConnectionString("Default");
builder.Services.AddDbContext<CoreDBContext>(options =>
{
options.UseSqlServer(connection, p =>
{
p.MigrationsAssembly("B12Core.Persistence");
});
}
);
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPreProcessorBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehaviour<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));
builder.Services.AddMediatR(p =>
{
p.AsTransient();
}, typeof(CreateMessageCommand).GetTypeInfo().Assembly);
}
}
Full Error
System.Private.CoreLib: Exception while executing function: Function1. Microsoft.EntityFrameworkCore: A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe. This could also be caused by a nested query being evaluated on the client, if this is the case rewrite the query avoiding nested invocations.
Solved it by changing the db context injection lifetime to ServiceLifetime.Transient
builder.Services.AddDbContext<CoreDBContext>(options =>
{
options.UseSqlServer(connection, p =>
{
p.MigrationsAssembly("Presistence");
});
},ServiceLifetime.Transient
);
I feel quite uncomfortable with the MongoClient class, certainly because I don't exactly understand what it is and how it works.
The first call to MongoClient.createShared will actually create the
pool, and the specified config will be used.
Subsequent calls will return a new client instance that uses the same
pool, so the configuration won’t be used.
Does that mean that the pattern should be:
In startup function, to create the pool, we make a call
mc = MongoClient.createShared(vx, config, "poolname");
Is the returned value mc important for this first call if it succeeds? What is its value if the creation of the pool fails? The documentations doesn't say. There is a socket exception if mongod is not running, but what about the other cases?
In another place in the code (another verticle, for example), can we write mc = MongoClient.createShared(vx, new JsonObject(), "poolname"); to avoid to systematically need to access shared objects.
Again, In another verticle where we need to access the database, should we define MongoClient mc
as a class field in which case it will be released to the pool only in the stop() method, or
shouldn't it be a variable populated with MongoClient.createShared(...) and de-allocated with mc.close() once we don't need the connection any more in order release it again to the pool ?
What I would write is as follows
// Main startup Verticle
import ...
public class MainVerticle extends AbstractVerticle {
...
#Override
public void start(Future<Void> sf) throws Exception {
...
try {
MongoClient.createShared(vx, config().getJsonObject("mgcnf"), "pool");
}
catch(Exception e) {
log.error("error error...");
sf.fail("failure reason");
return;
}
...
sf.complete();
}
...some other methods
}
and then, in some other place
public class SomeVerticle extends AbstractVerticle {
public void someMethod(...) {
...
// use the database:
MongoClient mc = MongoClient.createShared(vx, new JsonObject(), "pool");
mc.save(the_coll, the_doc, res -> {
mc.close();
if(res.succeeded()) {
...
}
else {
...
}
}
...
}
...
}
Does that make sense ? Yet, this is not what is in the examples that I could find around the internet.
Don't worry about pools. Don't use them. They don't do what you think they do.
In your start method of any verticle, set a field (what you call a class field, but you really mean instance field) on the inheritor of AbstractVerticle to MongoClient.createShared(getVertx(), config). Close the client in your stop method. That's it.
The other exceptions you'll see are:
Bad username/password
Unhealthy cluster state
The Java driver has a limit of 500 or 1,000 connections (depending on version), you'll receive an exception if you exceed this connection count
Both will be propagated up from the driver wrapped in a VertxException.
I am trying to access data from different databases based on a runtime variable. For this purpose, I have a custom implementation of MongoOperations. My implementation is same as MongoTemplate except my getDb() method looks like below:
public DB getDb() {
return mongoDbFactory.getDb(PropertyManager.getCurrentTenant().getCode());
}
While reading data in a transaction, I am getting below error:
[TransactionSynchronizationUtils] - TransactionSynchronization.beforeCompletion threw exception
java.lang.IllegalStateException: No value for key [Mongo: localhost/127.0.0.1:27017] bound to thread
It appears harmless as this exception is only logged:
public static void triggerBeforeCompletion() {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
try {
synchronization.beforeCompletion();
}
catch (Throwable tsex) {
logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex);
}
}
}
But I am having a hard time figuring out why this is happening. Any clues?
Versions:
spring-data-mongodb: 1.2.3.RELEASE
spring-tx: 4.0.5.RELEASE
There was a bug raised for similar issue which was fixed in 1.1 GA release.
It worked by extending
SimpleMongoDbFactory: returning custom DB in DB getDb(String dbName).
MongoTemplate: Supplying above factory.
I have an Asp.Net MVC 4 project using Entity Framework 5.0.
For some reason my DefaultConnectionFactory is not being called when I execute EF code from a Threadpool thread.
I setup my connection factory in the Application_Start event of my Asp.Net MVC project:
Database.DefaultConnectionFactory = new WebConnectionFactory();
This runs successfully on all data access code so far.
I have a Parallel.ForEach loop with a call to some EF code. For some reason the DefaultConnectionFactory is never hit when called. This factory is critical for my code to execute, so the call fails (it uses its default connection string).
Any ideas why this DefaultConnectionFactory would not be called when run on a new thread?
You need to get the connection that you built for each call that you are wanting to use. For example using the following code.
private static void UsingCustomConnection()
{
using (var conn = Database.DefaultConnectionFactory.CreateConnection("YourDbName"))
{
using (var context = new YourContext(conn))
{
context.Destinations.Add(new Destination {Name = "Colorado"});
context.SaveChanges();
}
}
}
You will need to setup this in YourContext
public YourContext(DbConnection connection)
: base(connection, contextOwnsConnection: false)
{
}
I have a FunctionalTest that tests posting to a controller and then does asserts on the model objects to make sure the controller did it's job, like so:
#Test
public void editUser(){
Logger.debug("Edit user test");
createNewUser();
final User user = User.<User>findAll().get(0);
POST("/ManageUser/save", ImmutableMap.of(
"user.id", user.getId().toString(),
"user.username", "test",
"user.email", "test#example.com",
"user.fullName", "Test Different"
));
User.em().flush();
User.em().clear(); // this is required so that it works on the mem DB
assertEquals(1, User.findAll().size());
assertEquals("Test Different", User.<User>findAll().get(0).fullName);
final User userAfterSave = User.<User>findAll().get(0);
assertFalse("New user should not be admin.", userAfterSave.isAdmin);
}
This passes when I use the mem database
%test.db.url=jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0
However if I switch to mysql
%test.db=mysql://test:test#localhost/test
It fails on the second assert "Failure, expected:<Test [Differen]t> but was:<Test [Tes]t>". So when using mysql the controller doesn't persist the user properly.
What am I missing here, are there some options for transaction control that I need to change for this to work?
The controller just calls user.merge().save() to update the user, is this somehow wrong?
This is because the mem DB is not properly transactional, meaning the test thread gets new data every read. For mysql however the test thread read the user in createNewUser() meaning it's transaction had the previous version. It's not obvious but POST() starts a new thread with a separate transaction. To solve this swap out
User.em().flush();
User.em().clear();
for
JPAPlugin.closeTx(false);
JPAPlugin.startTx(false);
The later starts a new transaction.
Do your search in a separate job to be sure it correctly view the modifications (transaction isolation). Here is an example
private FeedbackType findFeedbackType(final String name) throws ExecutionException, InterruptedException {
return new Job<FeedbackType>() {
#Override
public FeedbackType doJobWithResult() throws Exception {
return FeedbackType.findByName(name);
}
}.now().get();
}
This is a private method of my Functional test and I call this method to get my object instead of directly invoking the model