Searchkick reindexing not picking up queued changes - searchkick

I'm following along with the Queuing section of the README which seems to work fine until I begin a re-index operation. I'm noticing that I don't seem to get new changes that have happened after the re-index is started. For example if I have a model Product:
class Product < ApplicationRecord
searchkick callbacks: :queue
end
Product.reindex(mode: :async)
While the index is still reindexing:
new_product = Product.create
Searchkick::ProcessQueueJob.perform_now(class_name: "Product")
new_product is not included in the new index
What is the best way to handle this so that new_product will be included in the new index?

Related

JPQL can not prevent cache for JPQL query

Two (JSF + JPA + EclipseLink + MySQL) applications share the same database. One application runs a scheduled task where the other one creates tasks for schedules. The tasks created by the first application is collected by queries in the second one without any issue. The second application updates fields in the task, but the changes done by the second application is not refreshed when queried by JPQL.
I have added QueryHints.CACHE_USAGE as CacheUsage.DoNotCheckCache, still, the latest updates are not reflected in the query results.
The code is given below.
How can I get the latest updates done to the database from a JPQL query?
public List<T> findByJpql(String jpql, Map<String, Object> parameters, boolean withoutCache) {
TypedQuery<T> qry = getEntityManager().createQuery(jpql, entityClass);
Set s = parameters.entrySet();
Iterator it = s.iterator();
while (it.hasNext()) {
Map.Entry m = (Map.Entry) it.next();
String pPara = (String) m.getKey();
if (m.getValue() instanceof Date) {
Date pVal = (Date) m.getValue();
qry.setParameter(pPara, pVal, TemporalType.DATE);
} else {
Object pVal = (Object) m.getValue();
qry.setParameter(pPara, pVal);
}
}
if(withoutCache){
qry.setHint(QueryHints.CACHE_USAGE, CacheUsage.DoNotCheckCache);
}
return qry.getResultList();
}
The CacheUsage settings affect what EclipseLink can query using what is in memory, but not what happens after it goes to the database for results.
It seems you don't want to out right avoid the cache, but refresh it I assume so the latest changes can be visible. This is a very common situation when multiple apps and levels of caching are involved, so there are many different solutions you might want to look into such as manual invalidation or even if both apps are JPA based, cache coordination (so one app can send an invalidation even to the other). Or you can control this on specific queries with the "eclipselink.refresh" query hint, which will force the query to reload the data within the cached object with what is returned from the database. Please take care with it, as if used in a local EntityManager, any modified entities that would be returned by the query will also be refreshed and changes lost
References for caching:
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Caching
https://www.eclipse.org/eclipselink/documentation/2.6/concepts/cache010.htm
Make the Entity not to depend on cache by adding the following lines.
#Cache(
type=CacheType.NONE, // Cache nothing
expiry=0,
alwaysRefresh=true
)

ExecuteStoreCommand "Delete" returning different record count to "DeleteObject" post delete, why?

Got a strange one here. I am using EF 6 over SQL Server 2012 and C#.
If I delete a record, using DeleteObject, I get:
//order.orderitem count = 11
db.OrderItem.DeleteObject(orderitem);
db.SaveChanges();
var order = db.order.First(r => r.Id == order.id);
//Order.OrderItem count = 10, CORRECT
If I delete an Order Item, using ExecuteStoreCmd inline DML, I get:
//order.orderitem count = 11
db.ExecuteStoreCommand("DELETE FROM ORDERITEM WHERE ID ={0}", orderitem.Id);
var order = db.Order.First(r => r.Id == order.id);
//order.orderitem count = 11, INCORRECT, should be 10
So the ExecuteStoreCommand version reports 11, however the OrderItem is definitely deleted from the DB, so it should report 10. Also I would have thought First() does an Eager search thus repopulating the "order.orderitem" collection.
Any ideas why this is happening? Thanks.
EDIT: I am using ObjectContext
EDIT2: This is the closest working solution I have using "detach". Interestingly the "detach" actually takes about 2 secs ! Not sure what it is doing, but it works.
db.ExecuteStoreCommand("DELETE FROM ORDERITEM WHERE ID ={0}", orderitem.Id);
db.detach(orderitem);
It would be quicker to requery and repopulate the dataset. How can I force a requery? I thought the following would do it:
var order = db.order.First(r => r.Id == order.id);
EDIT3: This seems to work to force a refresh post delete, but still take about 2 secs:
db.Refresh(RefreshMode.StoreWins,Order.OrderItem);
I am still not really understanding why one cannot just requery as a Order.First(r=>r.id==id) type query oftens take much less than 2 secs.
This would likely be because the Order and it's order items are already known to the context when you perform the ExecuteStoredCommand. EF doesn't know that the command relates to any cached copy of Order, so the command will be sent to the database, but not update any loaded entity state. WHere-as the first one would look for any loaded OrderItem, and when told to remove it from the DbSet, it would look for any loaded entities that reference that order item.
If you don't want to ensure the entity(ies) are loaded prior to deleting, then you will need to check if any are loaded and refresh or detach their associated references.
If orderitem represents an entity should just be able to use:
db.OrderItems.Remove(orderitem);
If the order is loaded, the order item should be removed automatically. If the order isn't loaded, no loss, it will be loaded from the database when requested later on and load the set of order items from the DB.
However, if you want to use the SQL execute approach, detaching any local instance should remove it from the local cache.
db.ExecuteStoreCommand("DELETE FROM ORDERITEM WHERE ID ={0}", orderitem.Id);
var existingOrderItem = db.OrderItems.Local.SingleOrDefault(x => x.Id == orderItem.Id);
if(existingOrderItem != null)
db.Entity(existingOrderItem).State = EntityState.Detached;
I don't believe you will need to check for the orderItem's Order to refresh anything beyond this, but I'm not 100% sure on that. Generally though when it comes to modifying data state I opt to load the applicable top-level entity and remove it's child.
So if I had a command to remove an order item from an order:
public void RemoveOrderItem(int orderId, int orderItemId)
{
using (var context = new MyDbContext())
{
// TODO: Validate that the current user session has access to this order ID
var order = context.Orders.Include(x => x.OrderItems).Single(x => x.OrderId == orderId);
var orderItem = order.OrderItems.SingleOrDefault(x => x.OrderItemId == orderItemId);
if (orderItem != null)
order.OrderItems.Remove(orderItem);
context.SaveChanges();
}
}
The key points to this approach.
While it does mean loading the data state again for the operation, this load is by ID so it's fast.
We can/should validate that the data requested is applicable for the user. Any command for an order they should not access should be logged and the session ended.
We know we will be dealing with the current data state, not basing decisions on values/data from the point in time that data was first read.

Entity Framework delete without select and Optimistic Concurrency

I am trying to delete a record if it exists using Entity Framework - I want to generate something like
delete from tbl where id = 1
I don't care whether any records exist, I just want to delete them if they do. I should be able to do with without selecting them first like this:
var record = new tbl { id = 1, rel = new tbl_related { tbl_id = 1 } };
context.tbl.Attach(tbl);
context.tbl.Remove(tbl);
context.SaveChanges();
The syntax might not exactly be there because this isn't a copy and paste, and I've added the foreign key relationship to show that I'm taking this into account.
On running this code the correct SQL is generated by EF and run but I get a System.Data.Entity.Infrastructure.DbUpdateConcurrencyException saying "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded".
However, I'm fully expecting zero rows to be deleted a lot of the time. Any ideas on how I can get around this?
I've been pointed to How to ignore a DbUpdateConcurrencyException when deleting an entity as a possible duplicate. This suggests handling the DbUpdateConcurrencyException which I am now doing (and ignoring), but that post talks about trying to delete rows that might have been deleted - I want to delete rows that might never have existed. I know this might just be a question of semantics because the resolution is the same but I want to highlight that I am doing something that I see as perfectly reasonable and the Framework is not handling it very well.
Thanks
There is an extension for EF on github that allows you to update and delete entities with a single call to the database.
With this extension you can do something like this:
context.tbl.Delete(t => t.Id == 1);
A single call should eliminate your DbUpdateConcurrencyException.

Adding an entity to the Breeze saveMap which comes from DB (not a totally new entity)

I use Breeze in my asp.net application with the Durandal SPA template.
I need to add an entity to the saveMap which already exists in DB.
Let's take this simple example: the page display an invoice and the invoice's lines. The user add a new invoice's line and click the save button. The SaveChanges controller's action is triggered with ONLY the modified invoice's line. Server side, the total is recalculated and the invoice's total must be modified. But this total is located on the invoice entity so we need to add the invoice's entity to the saveMap.
I found a way to proceed to add a new entity to the saveMap here: Breeze BeforeSaveEntities: how to modify savemap
But the suggested solution is used to add a new entity to the saveMap (Added state) which will create a new record on DB. This is not what I need. I need to add a new entity to the saveMap which (Modified state) will be get the data from DB.
I tried like this:
int invoiceId = 1234;
dc.Configuration.ProxyCreationEnabled = false; // don't forget this!
EFContextProvider<BreezeContext> cp = new EFContextProvider<BreezeContext>();
var acc = dc.Invoices.Where(x => x.Id == invoiceId).FirstOrDefault();
ei = cp.CreateEntityInfo(acc, Breeze.WebApi.EntityState.Modified);
invoices = new List<EntityInfo>();
saveMap.Add(typeof(Invoice), invoices);
invoices.Add(ei);
So far so good.
Then I need to add the total property to the OriginalValuesMap (otherwise modification will not be updated):
ei.OriginalValuesMap.Add("TotalExclVAT", invoice.TotalExclVAT);
**This don't work: ei.OriginalValuesMap is null and so I cannot add a new key inside.
I don't know if this is the right way to proceed. Hope my explanations are clear enough.
Thanks for your help.
UPDATE
As suggested by Jay:
ei.ForceUpdate = true;
No need to take care of OriginalValuesMap in this case.
I haven't yet had a chance to dig into this yet, but have you looked at the EntityInfo.ForceUpdate property.
This property may be used to force a server side update of an entire entity when server side modification has been made to an existing entity. May be used in place of explicitly updating the EntityInfo.OriginalValuesMap.
So far we've only documented this in the release notes so it's understandable how it might be missed.

Entity Framework 4.1 Insert, Update Delete ASP.NET MVC3

I have a wizard where I am in updatemode. During this mode, I can actually insert, delete or update various records in the model. I am passing by reference to m CRUD methods. eg. MyMethod(ref Project project)
I was able to update by attaching my project to the context but when I also need to Delete during the same tranaction, update and delete do not do anything, How am I supposed to handle delete?
I do the following which does not work.
var FoundProjectUser = (from m in UserRoles where m.UserProfileId == member.UserProfileId select m);
if (FoundProjectUser.Count() == 0)
{
project.ProjectTeams.Remove(member);
}
ANSWER FOUND:
I found the problem. The problem is that in edit mode, the project is not attached to the context. I need to delete from the DBContext and not the project. Like this.Context.ProjectTeams.Remove(member);
.Remove will only flag the row for removal. You have to do entity.SaveChanges() to persist the changes.