How does #Lock(LockModeType.PESSIMISTIC_WRITE) works on method findByUuid - spring-data-jpa

I call this method in few threads and write to sessionId parameter the same value each time:
public SessionEntity getOrCreateSession(String sessionId) {
Optional<SessionEntity> optSession = sessionRepository.findByUuid(sessionId);
if (optSession.isPresent()) {
return optSession.get();
}
SessionEntity session = create();
SessionEntity savedSession = sessionRepository.save(session);
}
I also have not constraints on field sessionId in db. After preccessing of few threads I have many records in db with the same sessionId. And I try to use #Lock annotation. I put it on repository method like this:
#Repository
public interface SessionRepository extends JpaRepository<SessionEntity, Long> {
#Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<SessionEntity> findByUuid(String uuid);
}
But it doesn't help, I keep getting many records in db with the save sessionId. This my test for creating few threads and calling method:
var uuid = UUID.randomUUID();
ExecutorService executor = Executors.newFixedThreadPool(100);
List<Callable<SessionEntity>> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add(() ->
sessionService.getOrCreateClientSession(uuid));
}
executor.invokeAll(list);
How should I use annotation #Lock for my case to have only one record in DB?

Related

storing object in cosmos db returns bad request?

I seem to be unable to store a simple object to cosmos db?
this is the database model.
public class HbModel
{
public Guid id { get; set; }
public string FormName { get; set; }
public Dictionary<string, object> Form { get; set; }
}
and this is how I store the data into the database
private static void SeedData(HbModelContext dbContext)
{
var cosmosClient = dbContext.Database.GetCosmosClient();
cosmosClient.ClientOptions.AllowBulkExecution = true;
if (dbContext.Set<HbModel>().FirstOrDefault() == null)
{
// No items could be picked hence try seeding.
var container = cosmosClient.GetContainer("hb", "hb_forms");
HbModel first = new HbModel()
{
Id = Guid.NewGuid(),//Guid.Parse(x["guid"] as string),
FormName = "asda",//x["name"] as string,
Form = new Dictionary<string, object>() //
}
string partitionKey = await GetPartitionKey(container.Database, container.Id);
var response = await container.CreateItemAsync(first, new PartitionKey(partitionKey));
}
else
{
Console.WriteLine("Already have data");
}
}
private static async Task<string> GetPartitionKey(Database database, string containerName)
{
var query = new QueryDefinition("select * from c where c.id = #id")
.WithParameter("#id", containerName);
using var iterator = database.GetContainerQueryIterator<ContainerProperties>(query);
while (iterator.HasMoreResults)
{
foreach (var container in await iterator.ReadNextAsync())
{
return container.PartitionKeyPath;
}
}
return null;
}
but when creating the item I get this error message
A host error has occurred during startup operation '3b06df1f-000c-4223-a374-ca1dc48d59d1'.
[2022-07-11T15:02:12.071Z] Microsoft.Azure.Cosmos.Client: Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: 24bac0ba-f1f7-411f-bc57-3f91110c4528; Reason: ();.
Value cannot be null. (Parameter 'provider')
no idea why it fails?
the data should not be formatted incorreclty?
It also fails in case there is data in the dictionary.
What is going wrong?
There are several things wrong with the attached code.
You are enabling Bulk but you are not following the Bulk pattern
cosmosClient.ClientOptions.AllowBulkExecution = true is being set, but you are not parallelizing work. If you are going to use Bulk, make sure you are following the documentation and creating lists of concurrent Tasks. Reference: https://learn.microsoft.com/azure/cosmos-db/sql/tutorial-sql-api-dotnet-bulk-import#step-6-populate-a-list-of-concurrent-tasks. Otherwise don't use Bulk.
You are blocking threads.
The call to container.CreateItemAsync(first, new PartitionKey("/__partitionKey")).Result; is a blocking call, this can lead you to deadlocks. When using async operations (such as CreateItemAsync) please use the async/await pattern. Reference: https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#avoid-using-taskresult-and-taskwait
The PartitionKey parameter should be the value not the definition.
On the call container.CreateItemAsync(first, new PartitionKey("/__partitionKey")) the Partition Key (second parameter) should be the value. Assuming your container has a Partition Key Definition of /__partitionKey then your documents should have a __partitionKey property and you should pass the Value in this parameter of such property in the current document. Reference: https://learn.microsoft.com/azure/cosmos-db/sql/troubleshoot-bad-request#wrong-partition-key-value
Optionally, if your documents do not contain such a value, just remove the parameter from the call:
container.CreateItemAsync(first)
Be advised though that this solution will not scale, you need to design your database with Partitioning in mind: https://learn.microsoft.com/azure/cosmos-db/partitioning-overview#choose-partitionkey
Missing id
The model has Id but Cosmos DB requires id, make sure the content of the document contains id when serialized.

How to optimize SQL query in Anylogic

I am generating Agents with parameter values coming from SQL table in Anylogic. when agent is generated at source I am doing a v look up in table and extracting corresponding values from table. For now it is working perfectly but it is slowing down the performance.
Structure of Table looks like this
I am querying the data from this table with below code
double value_1 = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.avg_value)).get(0);
double value_min = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.min_value)).get(0);
double value_max = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.max_value)).get(0);
// Fetch the cluster number from account table
int cluster_num = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.cluster)).get(0);
int act_no = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.actno)).get(0);
String pay_term = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.pay_term)).get(0);
String pay_term_prob = (selectFrom(account_details)
.where(account_details.act_code.eq(z))
.list(account_details.pay_term_prob)).get(0);
But this is very slow and wants to improve the performance. someone mentioned that we can create a Java class and then add the table into collection . Is there any example where I can refer. I am finding it difficult to put entire code.
I have created a class using below code:
public class Customer {
private String act_code;
private int actno;
private double avg_value;
private String pay_term;
private String pay_term_prob;
private int cluster;
private double min_value;
private double max_value;
public String getact_code() {
return act_code;
}
public void setact_code(String act_code) {
this.act_code = act_code;
}
public int getactno() {
return actno;
}
public void setactno(int actno) {
this.actno = actno;
}
public double getavg_value() {
return avg_value;
}
public void setavg_value(double avg_value) {
this.avg_value = avg_value;
}
public String getpay_term() {
return pay_term;
}
public void setpay_term(String pay_term) {
this.pay_term = pay_term;
}
public String getpay_term_prob() {
return pay_term_prob;
}
public void setpay_term_prob(String pay_term_prob) {
this.pay_term_prob = pay_term_prob;
}
public int cluster() {
return cluster;
}
public void setcluster(int cluster) {
this.cluster = cluster;
}
public double getmin_value() {
return min_value;
}
public void setmin_value(double min_value) {
this.min_value = min_value;
}
public double getmax_value() {
return max_value;
}
public void setmax_value(double max_value) {
this.max_value = max_value;
}
}
Created collection object like this:
Pls provide an reference to add this database table into collection as a next step. then I want to query the collection based on the condition
You are on the right track here!
Every time you access the database to read data there is a computational overhead. So the best option is to access the database only once, at the start of the model. Create all the objects you need, store other data you will need later into Java classes, and then use the Java classes.
My suggestion is to create a Java class for each row in your table, like you have done. And then create a map object - like you have done, but with the key as String and the value as this new object.
Then on model start you can populate this map as follows:
List<Tuple> rows = selectFrom(customer).list();
for (Tuple row : rows) {
Customer customerData = new Customer(
row.get( customer.act_code ),
row.get( customer.actno ),
row.get( customer.avg_value )
);
mapOfCustomerData.put(customerData.act_code, customerData);
}
Where mapOfCustomerData is a linkedHashMap and customer is the name of the table
See the model created in this blog post for more details and an example on using a scenario object to store all the data from the Database in a separate object
Note: The code above is just an example - read this blog post for more details on using the AnyLogic INternal Database
Before using Java classes, try this first: click the "index" tickbox for all columns that you query with a WHERE clause.

JPA : Update operation without JPA query or entitymanager

I am learning JPA, I found out that we have some functions which is already present in Jparepository like save,saveAll,find, findAll etc. but there is nothing like update,
I come across one scenario where I need to update the table, if the value is already present otherwise I need to insert the record in table.
I created
#Repository
public interface ProductInfoRepository
extends JpaRepository<ProductInfoTable, String>
{
Optional<ProductInfoTable> findByProductName(String productname);
}
public class ProductServiceImpl
implements ProductService
{
#Autowired
private ProductInfoRepository productRepository;
#Override
public ResponseMessage saveProductDetail(ProductInfo productInfo)
{
Optional<ProductInfoTable> productInfoinTable =
productRepository.findByProductName(productInfo.getProductName());
ProductInfoTable productInfoDetail;
Integer quantity = productInfo.getQuantity();
if (productInfoinTable.isPresent())
{
quantity += productInfoinTable.get().getQuantity();
}
productInfoDetail =
new ProductInfoTable(productInfo.getProductName(), quantity + productInfo.getQuantity(),
productInfo.getImage());
productRepository.save(productInfoDetail);
return new ResponseMessage("product saved successfully");
}
}
as you can see, I can save the record if the record is new, but when I am trying to save the record which is already present in table it is giving me error related to primarykeyviolation which is obvious. I checked somewhat, we can do the update by creating the entitymanager object or jpa query but what if I dont want to use both of them. is there any other way we can do so ?
update I also added the instance of EntityManager and trying to merge the code
#Override
public ResponseMessage saveProductDetail(ProductInfo productInfo)
{
Optional<ProductInfoTable> productInfoinTable =
productRepository.findByProductName(productInfo.getProductName());
ProductInfoTable productInfoDetail;
Integer price = productInfo.getPrice();
if (productInfoinTable.isPresent())
{
price = productInfoinTable.get().getPrice();
}
productInfoDetail =
new ProductInfoTable(productInfo.getProductName(), price, productInfo.getImage());
em.merge(productInfoDetail);
return new ResponseMessage("product saved successfully");
but no error, no execution of update statements in log, any possible reasons for that ?
}
I suspect you need code like this to solve the problem
public ResponseMessage saveProductDetail(ProductInfo productInfo)
{
Optional<ProductInfoTable> productInfoinTable =
productRepository.findByProductName(productInfo.getProductName());
final ProductInfoTable productInfoDetail;
if (productInfoinTable.isPresent()) {
// to edit
productInfoDetail = productInfoinTable.get();
Integer quantity = productInfoDetail.getQuantity() + productInfo.getQuantity();
productInfoDetail.setQuantity(quantity);
} else {
// to create new
productInfoDetail = new ProductInfoTable(productInfo.getProductName(),
productInfo.getQuantity(), productInfo.getImage());
}
productRepository.save(productInfoDetail);
return new ResponseMessage("product saved successfully");
}

Datanucleus. Occasional javax.jdo.JDODetachedFieldAccessException: You have just attempted to access field "linkedObject"

We use Java Datanucleus 5.0.2 with JDO.
We get occasional exception while several threads are retrieving the same information and NO thread is changing this particular "linkedObject" reference.
NOTE: The object is retrieved using the fetch plan and logs show that.
It is hard to write a test case for this to fail as it is a race condition. But nevertheless I want to ask if someone has experienced it?
Caused by: javax.jdo.JDODetachedFieldAccessException: You have just attempted to access field "linkedObject" yet this field was not detached when you detached the object. Either dont access this field, or detach it when detaching the object.
at com.company.BaseClass.dnGetlinkedObject(BaseClass.java)
at com.company.BaseClass.getLinkedObject(BaseClass.java:71)
...
I can see three threads calling this method
private static <T> T getUniqueQueryJDO(final PersistenceManager pm, final JDOQLQuery query) throws PersistenceException {
try {
final javax.jdo.Query jdoQuery = setUpJDOQuery(pm, query);
jdoQuery.setUnique(true);
T result = null;
final T queryResult = (T) jdoQuery.executeWithMap(query.getMapValues());
if (queryResult != null) {
result = pm.detachCopy(queryResult);
}
jdoQuery.closeAll();
return result;
}
and one of them fails randomly
BaseClass.java
#PersistentDomainObject
#PersistenceCapable(table = "BaseClass", detachable = TRUE)
#Inheritance(strategy = InheritanceStrategy.SUPERCLASS_TABLE)
#FetchGroups(
...
#FetchGroup(name = FETCH_LINKED_OBJECT, members = {#Persistent(name = "linkedObject")})})
...
public class BaseClass {
...
public static final String FETCH_LINKED_CLASS = "FETCH_NAME";
...
#Persistent(defaultFetchGroup = FALSE, columns = {#Column(name = "linkedObjectId", allowsNull = FALSE)}, nullValue = NullValue.EXCEPTION)
private LinkedClass linkedObject;
...
public LinkedClass getLinkedObject() {
return linkedObject;
}
}

Entity Framework Updating with Stub causes Primary Key Violation

I have the following common tables with the relationships setup in a many to many fashion in my entity model:
Users - UserCodePK, UserName
UserGroups - UserCodeFK,GroupCodeFK
Groups - GroupCodePK,GroupDescription
My Code when trying to add a user:
public static string CreateUser(User user)
{
using (var dbContext = new DCSEntities())
{
User u = new User
{
UserCodePK = "NewUser",
txtUserName = "New User Name
};
u.Groups.Add(new UserGroup {GroupCode = "ADMIN"});
u.Groups.Add(new UserGroup {GroupCode = "SUPER"});
dbContext.Users.AddObject(user);
dbContext.SaveChanges();
}
}
The error that I'm getting is :
"Violation of PRIMARY KEY constraint 'PK_Groups'. Cannot insert duplicate key in object 'dbo.Groups'. The duplicate key value is (ADMIN)"
Basically saying that I'm trying to add the group "ADMIN", which already exists in that table. I thought that by using the stub as above, that I won't need to go the database to fetch the "ADMIN" group and add it to the User object.
Any advice on how to get rid of the error?
EDIT: My Completed Code Based on the Suggestions Below(I hope this is in the right place?)
UI Method
protected void CreateUser()
{
User user = new User();
user.UserCodePK = txtUserCode.Text;
user.UserName = txtUserName.Text;
List<UserGroup> userGroups = new List<UserGroup>();
for (int i = 0; i < chkListGroups.Items.Count; i++)
{
if (chkListGroups.Items[i].Selected == true)
{
userGroups.Add(new UserGroup { GroupCodePK = chkListGroups.Items[i].Value });
}
}
string userCode = BLL.UserFunctions.CreateUser(user, userGroups);
}
BLL Method
public static string CreateUser(User user, List<UserGroup> userGroups)
{
return UserDAL.CreateUser(user,userGroups);
}
DAL Method
public static string CreateUser(User user,List<UserGroup> userGroups)
{
using (var dbContext = new DCSEntities())
{
foreach (UserGroup g in userGroups)
{
var ug = new UserGroup { GroupCode = g.GroupCode };
dbContext.UserGroups.Attach(ug);
user.UserGroups.Add(ug);
}
dbContext.Users.AddObject(user);
dbContext.SaveChanges();
return user.UserCode;
}
}
It's a good idea to work with stubs. You only have to make sure that EF won't see them as new object, which you can do by attaching the stub to the context. Now EF will not give it the status Added.
var adminGroup = new UserGroup {GroupCode = "ADMIN"};
db.Groups.Attach(adminGroup);
...
u.Groups.Add(group);
If GroupCode is the primary key, EF will know how to associate the objects.