I'm trying to implement the pessimistic lock on my application and follow tutorials on Firedac explaining how to proceed. In my query I am informing Lock Mode lmPessimistic and Lock Point lpImmediate.
The lock works perfectly, not allowing me to access this record from another application instance. But it causes me a problem with transactions I do not know to solve, because never have explicitly worked with them.
Example:
I enter the screen using Edit;
Change the values;
I have to use a ApplyUpdates (0) CommitUpdates and Edit in the Query, because it is a master-detail;
Finally I record everything and close the screen.
Works perfectly, I refresh the record in my other open application and values are updated. But if I do the same test again, when I close the screen, the values are not updated in my second application. They are only updated when I close the first application.
I have found that what is causing it is the Query.Edit in the process. I think it's a problem with transactions, but did not put any type of component in the project, I know firedac is responsible for creating them for me. But I will have to set up anything else?
P.S.: I'm using CachedUpdates = True
Related
at the moment we are migrating the database component of our Delphi7 application from the BDE components to the AnyDAC Version 8.0.5 components.
The TTable (BDE) has the following behavior, before editing the record from another application instance (session):
The record is refreshed and changes are visible from other instances. The record will be refreshed in the method TBDEDataSet.InternalEdit.
The dataset is set into edit mode (DataSet.State = dsEdit)
Using the appropriate AnyDAC components (TADTable) the records does not reflect the changes done by other instances.
No special changes to TADConnection and TADTable are made.
Any help appreciated.
I cannot speak for BDE as I don't want to get in touch with it anymore, but what you've described I can read like:
Why does not AnyDAC refresh the tuple before editing starts?
If that is so, and correct me if I'm wrong, that would be quite against UX. Imagine, that you were a user of your own application and wanted to edit a certain tuple in a data grid view. You would click some edit button to enter editing mode, and the whole row would suddenly change in front of your eyes (or editor would be filled by different data than you've seen). Would you like this to happen?
If so, then I'm afraid you'll need to perform such refresh manually with AnyDAC (or FireDAC). The point here is that the engine either locks the tuple by transaction, or tracks the changes inside the internal storage whilst you're in the editing mode.
In neither case refreshes the tuple before editing starts (no matter which locking options you use). And I'm personally fine with this behavior as it could lead to what I've described above.
So how can I refresh the active tuple before editing starts then?
To refresh particular tuple to which the dataset cursor points before dataset editing starts you can call e.g. RefreshRecord from the BeforeEdit event, for example:
procedure TForm1.ADTable1BeforeEdit(DataSet: TDataSet);
begin
TADTable(DataSet).RefreshRecord;
end;
But then your database editing capability becomes a moving target (well, maybe it is already).
While developing with Firebase, I manually added a data record in the console but forgot one entry, which caused the app to crash. I've corrected the problem in the console but, because I was using Firebase's data persistence, the original data error persists, causing the crash again. If I switch the persistence off, everything is fine but the cached store isn't being updated. Has anyone had this problem and found a way to solve it?
I too have faced this issue, and have lost sleep over the thought of users being trapped in an endless crash-on-start-up loop.
As suggested, the opportunity for the issue to arise is created in the time window between the start up of an app and the subsequent arrival of cache updates from the Firebase server. If data is read from the cache during this time window, and then, if the data happens to be missing an expected value, and then, if the app uses the data in a way that assumes the data will not be nil, the app crashes. If the app crashes before the cache updates, the cache will never have the chance to update, and the user will be trapped in an endless loop (absent wiping the app's data from the device's memory).
Thus far, I've dealt with the issue by more strenuously guarding against the possibility of nil values in code that is called during start up. If nil is checked and inconveniently found, then, depending upon the situation, either (1) if possible and if it will not lead to further data corruption, the app substitutes an appropriate value in place of nil, or otherwise (2) the app goes into a wait mode for a couple of seconds, and then initiates a fresh read from the problem node, and then reattempts the start-up routine.
Perhaps the moral of the story is never assume that a value is non-nil or otherwise within an expected range. Either validate the value upon receipt or check the value at the time of intended use or both, and then handle errors accordingly.
The cache should update automatically. Since this happens in a background thread without, while it invokes your code on the main thread, I'd expect it to update the disk cache even if you application code crashes.
But if that doesn't happen, the fastest way to get back in a good state is to clear the app data for you application or to completely uninstall/reinstall.
I'm working with a Delphi 7 program written by my predecessor, and one of the things he was constantly doing in it is resetting the DateSeparator value and ShortDateFormat value. (Separator is '/', format is 'YYYY-MM-DD'.) The program in question is trying to check a set of records against a database, update them as needed, then write them out to a file for a handheld barcode scanner. Additionally, it is possible to select a set of records and display them as a report, done by passing their key values off inside a ShellExec string to a php page that actually runs the report.
Normally, this program works correctly, with no problems. However, on some machines (seems to be specific to Win7 boxes running IE9), if the reports are run first (opening IE from within the ShellExec call) the portion of the code that attempts to verify the records will end up with their dates screwed up - instead of appearing as YYYY-MM-DD as the Delphi ShortDateFormat calls specify, or as the M/d/yyyy of the Win7 default format, they'll appear in the output file as something screwy like 'ddyyyymm'. (The code is stripping the separators before writing to the file. The lack of them is expected.)
Given the weird conditions under which this problem occurs, I'm suspecting that something about opening IE9 is explicitly overwriting the program's attempt to reset the date format. Has anyone ever heard of anything like this before? And if so, what options do I have for resolving the issue? I don't have the authority to demand an IE upgrade on every machine our client uses this program on, and I'm not sure that would actually solve the problem anyway. I'm half afraid I'm going to have to reset the formats as the initial portion of every possible function and procedure in the program, and am seriously hoping there's something I can do that won't be so time-consuming and error-prone.
Try adding
Application . UpdateFormatSettings := false;
after
Application.Initialize;
in the DPR file.
From time-to-time the operating system will issue a WM_WININICHANGE message to all applications, to say that some global parameter, such as the user's regional settings, have changed. There are several things that can trigger this, usually it makes sense, but sometimes it can be a mystery. When a Delphi application receives this message, it will re-initialize the format settings. This is usually a good thing. Imagine you have a clock application that displays the date and time every second using DateTimeToStr(Now). The user goes into the Windows regional settings and changes their date format. Windows issues the WM_WININICHANGE message and your program's settings are automatically updated. The next time your clock display is updated, it will use the new correct settings.
But, if you are overriding the FormatSettings with application-specific values, this can be a big problem. Suddenly the settings that you worked so hard to set precisely as needed are thrown away and replaced with the default settings.
You can prevent this by setting UpdateFormatSettings to false.
Your application depends on the FormatSettings being as you've set them. And everything usually works perfectly. My hypothesis is that something is happening on those machines that's triggering the WM_WININICHANGE message.
Since I've started developing my Blackberry app, the biggest problems I've encountered all had to do with SQLite Databases.
Right now I'm putting my app through a stress test, and when problems pop up I address them by printing out statuses to the console and taking care of things line by line. Right now (after mashing buttons on my app) I received a "Database is locked" error and I'm not sure what to do.
It seems that once the database is locked it's locked for good until it is unlocked........ my question is how can I unlock it?? First of all, how can I check to see if it's locked??
I'm sure our users won't be mashing buttons like I did, but you never know. I want to account for every possible scenario.
Thanks
EDIT: This is what happens in my application..... When I launch it starts a thread, this thread performs a cleanup on one of my tables based on how old certain pieces of data are (uses DELETE). The thread then continues to get a USER object from my DB (read only), it then uses this USER object as a parameter to call a web service. The data retrieved from the web service is INSERTED into my database. (It's a little more complex than that as a few read/write operations are performed at this time. After that, the thread fires a callback method to update my UI.
This all works fine. I can exit the app WHILE the thread is running and relaunch and a flag will prevent it from starting a new instance of the same thread (unless the other one is done of course).
Now my problem: My app's home screen is a list of buttons, when the user clicks one of these buttons another, more detailed list is loaded (this requires a READ ONLY call to the database). When I launch the app (firing the web service calling thread) and then click a button on the main screen right away, the table gets locked. (Not always, sometimes it takes 4 or 5 tries, sometimes more, sometimes less). But if I keep doing this it WILL eventually lock making it impossible to make any calls to my DB, hence no more UI (which depends on the DB).
The DB call that populates the UI on the second screen is READ ONLY, can't I have as many of these as I need?? What causes the DB to lock?? What's the difference between a DB lock and File System error (12)??
I seemed to have fixed the problem. I was under the impression that if a read/write connection was open then a read-only connection could be created safely.
This doesn't seem to be the case. If I have a read/write connection open then no other connections can open until that one is finished.
I basically created one read/write connection, set a flag to identify it as open, and during my read connection use the same Database object if the flag is open, or create a read only if it's closed.
So far so good.
Sqlite does not support concurrent modification. In practice on BlackBerry, this means you can only open the database from one part of the code at a time. To maintain this one-at-a-time access, you need to close the database when you are done with it, as #AnkitRox points out.
However you also need to guard against concurrent access. Even if your code properly closes the database, it is possible for two different threads to access the database. In that case, you will need one to wait. In Java-ME this is typically accomplished through the 'synchronized' keyword, and using the same lock object for all database access.
Check properly that, you are opening and closing database before and after execution of query respectively.
Because if Database is going to open without closing it properly, then it gives errors.
I am running two instances of a program and they are both accessing a TAds table.
I want to reproduce a table lock for testing, but am having trouble.
I made a button that when clicked runs the code -
SomeTAdsTable.Edit;
When I click on the button on both instances of the program I am not getting any Advantage Table errors.
I am assuming it would be trying to edit the same record on both instances of the program.
Can anyone help me reproduce a table lock error with advantage?
Thanks
If the TAdsTable instances reference the same table, are are both positioned to the same record number and the record locking mode is set to lmPessimistic, then the second edit should result in a lock error.
However, the LockTable method might be applicable in this situation.