Currently an index has x documents written to it using an IndexWriter. After x have been written a check is made to ensure that all is okay on the persisted index.
var reader = IndexReader.Open(path);
If(!reader.NumDocs() == ExpectedCount)
{
// Do stuff
}
Is there a better way to achieve this? Opening an IndexReader constantly has to be expensive...
From http://blog.mikemccandless.com/2012/03/transactional-lucene.html
Lucene implements ACID properties:
- Atomicity: when you make changes
(adding, removing documents) in an IndexWriter session, and then
commit, either all (if the commit succeeds) or none (if the commit
fails) of your changes will be visible, never something in-between.
I think there is no good reason to doubt the above contract.
Related
I want to make it so that when you fill in a field (in case) X and go to a state, it is deleted (this field should be saved in the history, I think this is done by default). This is necessary so that the user does not have to be hitting the pencil and erasing the message that comes from another state.
As I saw with a Trigger it can be done, do you have any idea?
You don't need code for it, you could do it with config changes (workflow / flow / process builder). But if you're really after a trigger - something like that.
trigger CaseTrigger on Case(before update){
for(Case c : trigger.new){
Case old = trigger.oldMap.get(c.Id);
if(c.Status != old.Status){
c.Description = null; // whichever field you want to wipe
}
}
}
Edit about 0 code solutions
Look into workflows, flows and process builder. Actually if you're starting fresh maybe focus on flows, the other 2 are bit passe and SF recommends migrating away: https://admin.salesforce.com/blog/2021/go-with-the-flow-whats-happening-with-workflow-rules-and-process-builder
Have a look at these and if you're stuck: consider posting at dedicated https://salesforce.stackexchange.com. StackOverflow is really for code related stuff, you'll reach more admins over there.
https://trailhead.salesforce.com/content/learn/modules/flow-builder
https://trailhead.salesforce.com/en/content/learn/modules/platform-app-builder-certification-maintenance-winter-21/get-handson-with-flow-before-save-trigger-when-certain-record-changes-are-made
https://salesforce.stackexchange.com/questions/301451/trigger-flow-if-a-specific-field-on-the-updated-record-changed
https://help.salesforce.com/s/articleView?id=release-notes.rn_forcecom_flow_fbuilder_prior_values_flow.htm&type=5&release=230
I am using ag-grid/ag-grid-angular to provide an editable grid of data backed by a database. When a user edits a cell I want to be able to post the update to the backend service and if the request is successful update the grid and if not undo the user's changes and show an error.
I have approached this problem from a couple different angles but have yet to find the solution that meets all my requirements and am also curious about what the best practice would be to implement this kind of functionality.
My first thought was to leverage the cellValueChanged event. With this approach I can see the old and new values and then make a call to my service to update the database. If the request is successful then everything is great and works as expected. However, if the request fails for some reason then I need to be able to undo the user's changes. Since I have access to the old value I can easily do something like event.node.setDataValue(event.column, event.oldValue) to revert the user's changes. However, since I am updating the grid again this actually triggers the cellValueChanged event a second time. I have no way of knowing that this is the result of undoing the user's changes so I unnecessarily make a call to my service again to update the data even though the original request was never successful in updating the data.
I have also tried using a custom cell editor to get in between when the user is finished editing a cell and when the grid is actually updated. However, it appears that there is no way to integrate an async method in any of these classes to be able to wait for a response from the server to decide whether or not to actually apply the user's changes. E.g.
isCancelBeforeStart(): boolean {
this.service.updateData(event.data).subscribe(() => {
return false;
}, error => {
return true;
});
}
does not work because this method is synchronous and I need to be able to wait for a response from my service before deciding whether to cancel the edit or not.
Is there something I am missing or not taking in to account? Or another way to approach this problem to get my intended functionality? I realize this could be handled much easier with dedicated edit/save buttons but I am ideally looking for an interactive grid that is saving the changes to the backend as the user is making changes and providing feedback in cases where something went wrong.
Any help/feedback is greatly appreciated!
I understand what you are trying to do, and I think that the best approach is going to be to use a "valueSetter" function on each of your editable columns.
With a valueSetter, the grid's value will not be directly updated - you will have to update your bound data to have it reflected in the grid.
When the valueSetter is called by the grid at the end of the edit, you'll probably want to record the original value somehow, update your bound data (so that the grid will reflect the change), and then kick off the back-end save, and return immediately from the valueSetter function.
(It's important to return immediately from the valueSetter function to keep the grid responsive. Since the valueSetter call from the grid is synchronous, if you try to wait for the server response, you're going to lock up the grid while you're waiting.)
Then, if the back-end update succeeds, there's nothing to do, and if it fails, you can update your bound data to reflect the original value.
With this method, you won't have the problem of listening for the cellValueChanged event.
The one issue that you might have to deal with is what to do if the user changes the cell value, and then changes it again before the first back-end save returns.
onCellValueChanged: (event) => {
if (event.oldValue === event.newValue) {
return;
}
try {
// apiUpdate(event.data)
}
catch {
event.node.data[event.colDef.Field] = event.oldValue;
event.node.setDataValue(event.column, event.oldValue);
}
}
By changing the value back on node.data first, when setDataValue() triggers the change event again, oldValue and newValue are actually the same now and the function returns, avoiding the rather slow infinite loop.
I think it's because you change the data behind the scenes directly without agGrid noticing with node.data = , then make a change that agGrid recognises and rerenders the cell by calling setDataValue. Thereby tricking agGrid into behaving.
I would suggest a slightly better approach than StangerString, but to credit him the idea came from his approach. Rather than using a test of the oldValue/newValue and allowing the event to be called twice, you can go around the change detection by doing the following.
event.node.data[event.colDef.field] = event.oldValue;
event.api.refreshCells({ rowNodes: [event.node], columns: [event.column.colId] });
What that does is sets the data directly in the data store used by aggrid, then you tell it to refresh that grid. That will prevent the onCellValueChanged event from having to be called again.
(if you arent using colIds you can use the field or pass the whole column, I think any of them work)
I am getting following exception in a single box cq5 author environment.
javax.jcr.InvalidItemStateException: Item cannot be saved
because node property has been modified externally
more exception details:
Caused by: javax.jcr.InvalidItemStateException: Unable to update a stale item: item.save()
at org.apache.jackrabbit.core.ItemSaveOperation.perform(ItemSaveOperation.java:262)
at org.apache.jackrabbit.core.session.SessionState.perform(SessionState.java:216)
at org.apache.jackrabbit.core.ItemImpl.perform(ItemImpl.java:91)
at org.apache.jackrabbit.core.ItemImpl.save(ItemImpl.java:329)
at org.apache.jackrabbit.core.session.SessionSaveOperation.perform(SessionSaveOperation.java:65)
at org.apache.jackrabbit.core.session.SessionState.perform(SessionState.java:216)
at org.apache.jackrabbit.core.SessionImpl.perform(SessionImpl.java:361)
at org.apache.jackrabbit.core.SessionImpl.save(SessionImpl.java:812)
at com.day.crx.core.CRXSessionImpl.save(CRXSessionImpl.java:142)
at org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProvider.commit(JcrResourceProvider.java:511)
... 215 more
Caused by: org.apache.jackrabbit.core.state.StaleItemStateException: 3bec1cb7-9276-4bed-a24e-0f41bb3cf5b7/{}ssn has been modified externally
at org.apache.jackrabbit.core.state.SharedItemStateManager$Update.begin(SharedItemStateManager.java:679)
at org.apache.jackrabbit.core.state.SharedItemStateManager.beginUpdate(SharedItemStateManager.java:1507)
at org.apache.jackrabbit.core.state.SharedItemStateManager.update(SharedItemStateManager.java:1537)
at org.apache.jackrabbit.core.state.LocalItemStateManager.update(LocalItemStateManager.java:400)
at org.apache.jackrabbit.core.state.XAItemStateManager.update(XAItemStateManager.java:354)
at org.apache.jackrabbit.core.state.LocalItemStateManager.update(LocalItemStateManager.java:375)
at org.apache.jackrabbit.core.state.SessionItemStateManager.update(SessionItemStateManager.java:275)
at org.apache.jackrabbit.core.ItemSaveOperation.perform(ItemSaveOperation.java:258)
Here is the code sample:
adminResourceResolver = resourceResolverFactory
.getAdministrativeResourceResolver(null);
Resource fundPageResource = adminResourceResolver.getResource(page
.getPath() + "/jcr:content");
ModifiableValueMap homePageResourceProperties = fundPageResource
.adaptTo(ModifiableValueMap.class);
homePageResourceProperties.put("ssn",(person.getSsn());
adminResourceResolver.commit();
Any ideas ? It could be possible multiple threads accessing this code, as multiple authors on multiple pages calling this code from a authored component.
Thank you,
Sri
This is an error your see often in CQ5.5 (and lessens with each version upwards). The root cause of this issue is that multiple processes/services are modifying the same resource in roughly the same timespan (usually using different sessions, sometimes even with different users).
A small example to demonstrate perhaps. Session A and B both have a reference to Resource X. Session A modifies some properties on X, saves and commits, and is destroyed. This all goes smoothly. Session B still has a snapshot of the situation before A made modifications, session B makes modifications and all seems well UNTIL it tries to save. At this point, session B detects that it can't commit its changes because it doesn't have the latest node state. It has detected some other sessions made changes to the same node. In essence the current node state conflicts with modifications that session A has done and throws an ItemStale exception. The reason for this exception is the notion that the API doesn't know wether you want to keep the changes made by A, keep the changes made by the current session and discard the changes made by A, or merge them.
This error happens often with long running sessions and with workflow/listener combinations. Therefore the recommendation is to keep sessions as short as possible to prevent this kind of conflicts as much as possible.
One way to deal with this is to call session.refresh(keepChangesBoolean) before calling .save(). This instructs the current session to check for updates made by other sessions and deal with it according to the boolean flag you submit. This however is not a guarantee as it's still possible that between your refresh and your save call, yet another session has done the same. It only lowers the odds of this exception occurring.
Another way to deal with this is to retry again from scratch.
I need to log trace events during boot so I configure an AutoLogger with all the required providers. But when my service/process starts I want to switch to real-time mode so that the file doesn't explode.
I'm using TraceEvent and I can't figure out how to do this move correctly and atomically.
The first thing I tried:
const int timeToWait = 5000;
using (var tes = new TraceEventSession("TEMPSESSIONNAME", #"c:\temp\TEMPSESSIONNAME.etl") { StopOnDispose = false })
{
tes.EnableProvider(ProviderExtensions.ProviderName<MicrosoftWindowsKernelProcess>());
Thread.Sleep(timeToWait);
}
using (var tes = new TraceEventSession("TEMPSESSIONNAME", TraceEventSessionOptions.Attach))
{
Thread.Sleep(timeToWait);
tes.SetFileName(null);
Thread.Sleep(timeToWait);
Console.WriteLine("Done");
}
Here I wanted to make that I can transfer the session to real-time mode. But instead, the file I got contained events from a 15s period instead of just 10s.
The same happens if I use new TraceEventSession("TEMPSESSIONNAME", #"c:\temp\TEMPSESSIONNAME.etl", TraceEventSessionOptions.Create) instead.
It seems that the following will cause the file to stop being written to:
using (var tes = new TraceEventSession("TEMPSESSIONNAME"))
{
tes.EnableProvider(ProviderExtensions.ProviderName<MicrosoftWindowsKernelProcess>());
Thread.Sleep(timeToWait);
}
But here I must reenable all the providers and according to the documentation "if the session already existed it is closed and reopened (thus orphans are cleaned up on next use)". I don't understand the last part about orphans. Obviously some events might occur in the time between closing, opening and subscribing on the events. Does this mean I will lose these events or will I get the later?
I also found the following in the documentation of the library:
In real time mode, events are buffered and there is at least a second or so delay (typically 3 sec) between the firing of the event and the reception by the session (to allow events to be delivered in efficient clumps of many events)
Does this make the above code alright (well, unless the improbable happens and for some reason my thread is delayed for more than a second between creating the real-time session and starting processing the events)?
I could close the session and create a new different one but then I think I'd miss some events. Or I could open a new session and then close the file-based one but then I might get duplicate events.
I couldn't find online any examples of moving from a file-based trace to a real-time trace.
I managed to contact the author of TraceEvent and this is the answer I got:
Re the exception of the 'auto-closing and restarting' feature, it is really questions about the OS (TraceEvent simply calls the underlying OS API). Just FYI, the deal about orphans is that it is EASY for your process to exit but leave a session going. This MAY be what you want, but often it is not, and so to make the common case 'just work' if you do Create (which is the default), it will close a session if it already existed (since you asked for a new one).
Experimentation of course is the touchstone of 'truth' but I would frankly expecting unusual combinations to just work is generally NOT true.
My recommendation is to keep it simple. You need to open a new session and close the original one. Yes, you will end up with duplicates, but you CAN filter them out (after all they are IDENTICAL timestamps).
The other possibility is use SetFileName in its intended way (from one file to another). This certainly solves your problem of file size growth, and often is a good way to deal with other scenarios (after all you can start up you processing and start deleting files even as new files are being generated).
We have a HTTP end-point that takes a long time to run and can also be called concurrently by users. As part of this request, we update the model inside a synchronized block so that other (possibly concurrent) requests pick up that change.
E.g.
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
//Long running operation continues here. It can involve further changes to instance "m"
The reason for the synchronized block is to ensure that even concurrent requests get to pick up the latest status. However, the underlying JPA does not commit my changes (m.save()) until the request is complete. Since this is a long-running request, I do not want to wait until the request is complete and still want to ensure that other callers are notified of the change in status. I tried to call "m.em().flush(); JPA.em().getTransaction().commit();" after m.save(), but that makes the transaction unavailable for the subsequent action as part of the same request. Can I just given "JPA.em().getTransaction().begin();" and let Play handle the transaction from then on? If not, what is the best way to handle this use-case?
UPDATE:
Based on the response, I modified my code as follows:
MyModel m = null;
synchronized (lockObject) {
m = MyModel.findById(id);
if (m.status == PENDING) {
m.status = ACTIVE;
} else {
//render a response back to user that the operation is not allowed
}
m.save(); //Is not expected to be called unless we set m.status = ACTIVE
}
new MyModelUpdateJob(m.id).now();
And in my job, I have the following line:
doJob() {
MyModel m = MyModel.findById(id);
print m.status; //This still prints the old status as-if m.save() had no effect...
}
What am I missing?
Put your update code in a job an call
new MyModelUpdateJob(id).now().get();
thus the update will be done in another transaction that is commited at the end of the job
ouch, as soon as you add more play servers, you will be in trouble. You may want to play with optimistic locking in your example or and I advise against it pessimistic locking....ick.
HOWEVER, looking at your code, maybe read the article Building on Quicksand. I am not sure you need a synchronized block in that case at all...try to go after being idempotent.
In your case if
1. user 1 and user 2 both call that method and it is pending, then it goes to active(Idempotent)
If user 1 or user 2 wins, well that would be like you had the synchronization block anyways.
I am sure however you have a more complex scenario not shown here, BUT READ that article Building on Quicksand as it really changes the traditional way of thinking and is how google and amazon and very large scale systems operate.
Another option for distributed transactions across play servers is zookeeper which the big large nosql guys use BUT only as a last resort ;) ;)
later,
Dean