Force all users to disconnect from 2010 Access backend database - ms-access

We have multi user frontend/backend MS Access 2010 application. We added a process that will close remote frontends when we want to do work on backend such as compact and repair. This is timer based check on table field that if has certain value will close the application.
I do two checks to see if users are connected to database:
i have login/logout process and can see who is still logged in (its form based so is fallible eg they close form but frontend is still open).
i used .ldb file viewer to see if anything is still connected
Two questions:
is there any possibility that a connection to backed could exist if it wasn't viewable with ldb viewer?
is there any bullet proof 100% certain way to forcefully disconnect all connections from backend?

Users put "locks" into an LDB. An LDB is a kind of database of locks for MS Access.
Users also put "locks" onto LDB's and MDB's. These locks are part of the native database primitives provided by Windows and Windows networking.
If there are no locks IN the ldb, the user is not writing into the MDB.
If there are no locks ON the mdb, the user is not using that Windows/Networking service.
To get exclusive locking on an MDB, you need both. MSAccess won't let you exclusive lock while the LDB has entries showing someone else is using the database, and Windows won't give you an exclusive lock while it has locks showing someone else is using the database.
To 100% bulletproof empty the LDB, you delete the LDB. That is why Access always attempts to delete the LDB on close. If Access crashes, or the network is disconnected, or the PC or Server is turned off, the LDB will have data ("locks") that have not been deleted. You get rid of them by deleting the LDB.
Access won't be able to delete the LDB if a user still has Windows/Network locks on the LDB. Since Access is ignoring existing "lock" entries to clear broken "lock" entries, this is how Access/Windows prevents Access deleting the LDB while another user is still using it.
If EITHER the Windows locks or the Access/LDB "locks" exist, you can't get exclusive ownership. If ONLY the Access/LDB "locks" exist, you can delete the LDB, and then get exclusive ownership.
If ANY Windows/Network locks exist, nothing you do with Access can allow you to disconnect another person from the network. You need to go to the Server/PC that hosts the file, and force the disconnection. Access is not a network administration program that does this for you.
Last I looked, Windows Server was set by default to time-out broken file connections 15 minutes after the network session was lost. So if you turn off a PC, wait 15 minutes, then the Windows/Network locks will go (default timing). Alternatively: Admin tools, Computer Management,System Tools,Shared Folders, Sessions. Select the file you wish to unlock, and delete the session.

A fix exists, and I have it in place for an MS-Access Application I maintain. However, it's not convenient, and it's not simple to implement.
First, some background on locking files and the locked file.
The locking file is a useful tool but it's an indicator, not the actual lock: you can edit it but that has no effect.
Live MS-Access sessions can keep a file lock on the .accdb or .mdb back-end database file if they edit data, change an object (eg: index a table) or perform a maintenance task. The last two items in that list are rare - it takes some effort to affect another database file outside the one you've opened in your session, but it is sometimes observed in the wild - but closing those sessions should release the lock and almost always will.
Crashed MS-Access sessions can keep a file lock on the .accdb or .mdb back-end database file. Forced restarts of the offending machines should release the lock, and mostly will.
You can trace those using the lock file (.ldb or .laccdb)
Third-party reporting applications using ODBC or ADO to your Access database should have their connections set read-only, so that they can't lock the file. If you're lucky, the system owners use a dsn file you control, and you can see that setting.
Some reporting apps, like QLikView, are opaque interfaces that demand the full filename and nobody knows what happens next: maybe it locks, maybe it doesn't. You have no way of knowing.
Some reporting apps - and Excel Pivot Tables - may link to your db, and the owners simply don't tell you. If their connections are badly configured, they can lock your table while retrieving: and terminating the client session can leave the lock in place. Bored Excel users watching a slow pivot 'refresh' do this all the time.
Worse, some third-party systems may well need to write data: this should be avoided - go through the Access client application! - but you don't always get to make that decision.
If you're lucky, the connection object is configured correctly, so the machine ID is in the connection string, ensuring it'll turn up in the locking file and allow you to locate the machine with the lock...
Maybe they do that: often they don't.
...Note that row-locking queries, page-locking queries, and table-locking queries are functionally identical for the issue that matters to you: if you need to do something (like compact and repair) at the 'file' level, the file is locked against you. Until that lock is released, you're locked out.
You may or may not be able to identify the machine. You may or may not be able to force them off the network. They may or not release the lock successfully in normal operation, and they almost certainly won't if they crash out.
So: it's not a pretty picture.
Your Best solution, in a corporate environment, is to have a direct line to a network administrator with the 'finger' tool to identify who is locking the database file, the ability to contact the user, and the ability to disconnect the offending session.
The modes of failure are:
If your organisational structures do not allow you to establish that
line of communication, you're screwed.
If your organisation's network administrators do not have the ability
or the authority to do that in a useful timeframe, you're screwed.
If your organisation's network administrator chooses not to do it,
you're screwed.
However, I do have a fix in place for my MS-Access Application:
The workaround I use is extremely aggressive - not quite the 'nuclear option' of putting a shovel through the building's power supply, or giving in to the sysadmin's demands for human sacrifice, but deeply unpleasant and a ridiculous amount of work to implement:
Configure the clients - your MS-Access front-end applications - to
switch between named back-end databases easily.
Likewise, the dsn files you have written and allowed other apps to
use should be accessible to a pre-existing script to edit the
'DBQ='or 'Data Source=' clause to a new filename.
Grab a copy of the database file, take it down to a local temp
folder for the maintenance work you needed to do, and post it back
up to your application's 'Data' folder under a new name.
Now run the 'Change back-end' script or VBA function...
...And to hell with anyone else who's using the old, locked,
back-end file.
Hint: implementing your front-end application so that it's regularly polling the back-end database (or some other resource, your worst case scenario is a total lockout) for a 'Maintenance shutdown' or 'Change database' instruction is a smart thing to put in your 'Main Menu' form's timer event.
Also, there's code out there to automate switching the source db for linked tables.

"I do two checks to see if users are connected to database"
If you need to open the db exclusively, you can skip the other checks and just check whether you can do it.
Public Function CheckExclusive(ByVal pFullPath As String) As Boolean
Dim blnReturn As Boolean
Dim cn As Object
Dim strConnection As String
On Error GoTo ErrorHandler
strConnection = "Provider=" & _
CurrentProject.Connection.Provider & _
";Data Source=" & pFullPath & ";"
Set cn = CreateObject("ADODB.Connection")
cn.Mode = 12& ' adModeShareExclusive '
cn.Open strConnection
blnReturn = True
cn.Close
ExitHere:
On Error Resume Next
Set cn = Nothing
On Error GoTo 0
CheckExclusive = blnReturn
Exit Function
ErrorHandler:
blnReturn = False
GoTo ExitHere
End Function
Then call that function with the full path to your db file.
If CheckExclusive("C:\SomeFolder\YourDb.mdb") = True Then
' do what you need here which requires exclusive access: '
' make backup; compact; whatever '
End If

is there any possibility that a connection to backed could exist if it
wasn't viewable with ldb viewer?
Yes, I have had on numerous occasions had issues where the the backend was locked but I could not see anything with an ldb viewer. In these cases, we have even resulted in doing hard-shutdown on the machines that users access the DB from. Awful solution but in times of need you are sometimes desperate.
is there any bullet proof 100% certain way to forcefully disconnect
all connections from backend?
No, I have not been able to find a 100% way to force everyone out of the backend DB. The closest thing that I found to perfect was using Chris Symonds Timeout-Force Out code located on Utter Access
This database accomplishes several tasks:
Allows developer to go to any PC using db and force all users off a split db so that the developer can open db exclusively to
modify/maintain db.
Kicks users off db if they haven’t used db in a specified number of minutes set by developer.
Allows developer to specify a time each day to force all users off split db and close the db for backup, compile or whatever.
All functions are optional and may be bypassed with no extra coding, use only what you need for each db.
I implemented his code and it works about 99% of the time, but there are still those occasions where MS Access gets a bit touchy and I cannot see why the DB is locked or force-everyone out.

Related

How can I automatically disconnect idle users from a MS Access DB?

MS Office 365 ProPlus, Access 2007 - 2016
Here's the problem...
I have a MS Access DB on a shared file server which many users from many sites can access to make a few small edits on a weekly basis. Occasionally a user with an active connection will abruptly shut down their client server (their laptop) leaving a dangling connection to the DB which results in a DB in limbo which cannot be opened. Messages indicate that it's corrupted and the option to repair it just hangs. Not a big deal because I have a task scheduler job that backs-up (copies) the DB every 3 hours and I can just use the most recent backup to restore. But I can't even delete/clobber the DB with the backup because the dangling process is connected and Windows refuses to let me delete it (which is a problem in and of itself IMO).
The solution is to find the connection, kill it, delete the .accdb and the .laccdb, then replace it with the backup. But unfortunately, I don't have privs to find/kill the connection, so I have to engage someone in IT to go do that (can take a long time & the person I get often doesn't know how to do this).
What would really alleviate the problem is an automated DB disconnect for client processes that are idle for more than x minutes. I saw something similar to this using a form. But my users edit the cells in the tables directly (they like the ExCel style editing over forms). I searched DB options and don't see an option to set up a idle timeout/disconnect for the DB in general.
Question: Is there a way to set up an idle timeout like this which will close the DB connection after x minutes?
Question: Given the problem stated, is there another way to prevent these DB "corruptions". Everything I've read about this is along the lines of "educate your users to close Access when done". There are too many users and I doubt they'll pay attention.
Question: Access seems like a not-so robust DB for this, but the users really want the ExCel style interface (edit cells in records without forms). The record level locking mech seems to work well. And I love the fact that I can easily set up triggers and constraints to maintain data quality, query the thing with SQL, etc... (ExCel not so good for this). But if there's a better alternative than Access, I'm all ears.
Thanks for any ideas !
This is a pretty common problem, with no easy solutions.
There are two main approaches for this problem:
Use a hidden form with a timer that opens on startup. Have the form check every now and then if a form/datasheet is open, and if the active record or active control on that form/datasheet changes. If it doesn't for a period of time, close down Access.
The code required for this method is described here on MS docs, it only requires a few additions for datasheets (tables/queries).
This method has the limitation that you're shutting down Access for users that are multitasking. If they don't touch Access a while, it's gone, and that's not great UX.
Another limitation here is that there's a chance a user is kicked out unnecessarily, for example because he's moving data from another application and thus always has his cursor on a new record while actively using the database.
Start a timer on startup using WinAPI (could also be a hidden form with a timer instead). Use GetLastInputInfo to verify that the user hasn't sent any input for a set period on the timer. If it hasn't, then close down Access.
This has the limitation that any input (e.g. a mouse that moves a tiny bit when walking past an unsteady desk) will reset the timer.
If you have trouble implementing either of these, I can provide sample code.
Your better option - because of the limitations explained by Erik - would be (if not done already) to split the database into a frontend and a backend, convert the backend to SQL Server (Express edition is free to use), and distribute then frontend to the user's machines using a method as described in my article (if you don't have an account, browse to the link "Read the full article"):
Deploy and update a Microsoft Access application with one click
That would completely free you from the daunting tasks that keep you busy.
Here's something that shows prmoise, from
https://bytes.com/topic/access/answers/942650-can-you-force-access-quit-when-pc-goes-into-standby-mode
Idea is to run a vbs that runs access. If a power down event is sensed, it will disconnect the DB instead of leaving a dangling process.
Option Explicit
Dim objAccess, objWMIService, colItems
Set objAccess = CreateObject("Access.Application")
objAccess.Visible = True
objAccess.OpenCurrentDatabase "\\vausamd20\SCBUDV_Infra\dbg_taskdb.accdb"
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecNotificationQuery("Select * from
Win32_PowerManagementEvent")
Do While True
If colItems.NextEvent.EventType = 4 Then
objAccess.Quit
Set objAccess = Nothing
Set colItems = Nothing
Set objWMIService = Nothing
Exit Do
End If
Loop
Open to comments if anyone thinks this is a good or bad idea !

Microsoft Access - How to create a persistent connection to the linked database between back-end and front-end?

I have a connection speed problem when someone running a query from a Access database. The delay for loading data significantly increase when a user runs new queries after some minutes of not using the app.
I found this article for fixing this issue, I did all the steps but the codes were not working for me! I have one single database and the app working on multi-user environment (9 people).
Does anyone have any solutions/suggestions?
Thanks,
Peyman
Keep in mind that a persistent connection does not always fix slow network issues. However in a rather high % of cases it does.
The most easy way to “test” if a persistent connection will fix your issue(s) is to simply open up a linked table in your front end. (Any table will do as long as it is a linked table pointing to your back end). NOW run your code or query. If it runs fast then a persistent connection will help your case. If the above simple 3 second test does NOT fix performance, then a persistent connection while recommend and is a good idea – it will not fix your performance issue.
So try the above. If the above fixes your issues, then you can pursue one of several means to achieve a persistent connection.
Some simple ways to force or always have a connection open to the back end:
Simply open some form bound to a linked table. It could even be a “test” form. Right after you open the form, you can set its visible property = false.
Another way is to define a global record set variable and open that record set on start up to a table in the back end. Eg:
Set rst = currentdb.OpenRecordSet("select * from tblTest")
Keep in mind that if you using a non-compiled application (accDB as opposed to a compiled accDE) than any un-handled error will blow out such global variables and thus your persistent connection will be lost.
So before you go on a wild goose chase you can test with a persistent connection by simply opening a linked table in your front end, minimise it and then run your test code or whatever it is that is slow. If you don’t see a speed up, then the performance issue(s) you have will not be solved by using a persistent connection.

VB6 Database operations slow when multiple users connect to the database

I am currently using VB6 to connect to a MS access DB using DAO and I’m experiencing a very noticeable speed reduction when a 2nd user connects to the Database.
Here are the steps to reproduce:
Open the Database from computer A by logging into the software
Add records to the database via the software (takes about .4 seconds)
A second user logs into the software (Computer B), ie: this opens the database, displays todays transactions, but the user does nothing else
On Computer A, repeat the operation of adding records, now the operation takes approximately 6 seconds
Further info…
the operation continues to take aprox 6 seconds, even after Computer B logs out of the software
if you close and reopen the application from Computer A the operation returns to taking only .4 seconds to execute!
Any help would be greatly appreciated!
Thanks!
That is the way MS Access works. While it kind of supports multiple users, and kind of supports placing the DB on a file share so multiple PCs can access it, it does neither really well. And if you are doing both (multi-user and over a network to a file share) then I feel for your pain.
The answer is to run the upgrade wizard and convert this to an MS SQL Server instance. MS SQL Server Express edition is a good choice to replace Acess in the case. Note that you can still keep all of your code and reports etc you have in Access, only the data needs to be moved.
Just to be clear on the differences, in MS Access when you read data from the database, all of the data required to perform your query is read from a file by your program, no server-side processing is done. If that data resides on a network, you are pulling that data across your network. If there are multiple users, you have an additional overhead of locking. Each users program/process effectively dialogs with the program/process of the other users via file I/O (writing lock info into the networked file or files). And if the network I/O times out or has other issues then those files can become corrupted.
In SQL Server, it is the SQL Server engine that manages the data requests and only returns the data required. It also manages the locks and can detect when a client has disconnected or timed out to clean up, which reduces issues caused by multiple users on a network.
We had this problem with our VB3 / Jet DB 2.5 application when we transitioned to using newer file servers.
The problem is "opportunistic locking" : http://support.microsoft.com/kb/296264?wa=wsignin1.0
Albert is probably describing the same thing ; the server will permit one client exclusive access of a file, but when another chimes in, this exclusive access will "thrash" between them, causing delays as the client with the oplock flushes all it's local cache to the server before the other client can access the file.
This may also be why you're getting good performance with one client - if it takes an oplock, it can cache all the data locally.
This can also cause some nasty corruption if one of your clients has a power failure or drops off the network, because this flushing of the local cache to the server can be interrupted.
You used to be able to disable this (on the client - so you need to service ALL the clients) on Windows 2000 and XP as per the article, but after Vista SP2 it seems to be impossible.
The comments about not using Access / JetDB as a multi-user database are essentially correct - it's not a good architectural choice, especially in light of the above. DAO is also an obsolete library, even in the obsolete VB6. ADODB is a better choice for VB6, and should allow you some measure of database independence depending on how your app is written.
Since as you pointed out you get decent performance with one user on the system, then obviously your application by nature is not pulling too much data over the network, and we can't blame network speed here.
In fact what is occurring is the windows file share system is switching from single file share mode into multi-share file mode. This switching file modes causes a significant delay. And this also means that the 2nd or more user has to attempt to figure out and setup locks on the file.
To remove this noticable delay simply at the start of your application open what we call a persistent connection. A persistent connection is simply something that forces the network connection to remain open at all times, and therefore this significant delay in switching between two file modes for file share is eliminated. You now find that performance with two users should be the same as one (assuming one user is idle and not increasing network load). So at application startup time, open a back end table to a global var and KEEP that table open at all times.

How to compress an MS Access database

I have an .mdb file which is 70MB.
After deleting all records contained in the file, the size remains 70MB.
How do I make my .mdb file smaller?
Every database engine that has ever existed needs regular maintenance operations run on them to optimize data storage and to recover slack space. Back in xBase days, you ran a PACK command to remove deleted rows, for instance. On SQL Server, you run scripts to shrink the actual data files for the same reasons.
Why does every database engine do this?
Because it would be a huge performance hit if every write to the database had to rewrite the whole file in optimized order. Consider a database that stores each data table in a separate file. If a table has 10000 records, and you delete the 5000th record, to get rid of slack space, you'd have to rewrite the whole second half of the data file. Instead, every database uses some form of marking the space used as unused and discardable the next time the optimize operations are run on the data table.
Jet/ACE is no different in this regard than any other database engine and any application using a Jet/ACE database as a data store should have regular maintenance operations scheduled, including a backup and then a compact.
There are some issues with this in Jet/ACE that aren't present in server database engines. Specifically, you can't compact unless all users have closed their connections to the data file. In a server database, the users connect to the database engine's server-side process, and that server-side demon is the only "user" of the actual data files in which the data is stored. Thus, the server demon can decide when to perform the optimization and maintenance routines, since it's entirely in control of when the data files are in use or not.
One common problem with Access applications is that users will leave their application open on their computers and leave the office for the day, which means that when you run your compact operation, say at 2:00am, the file is still open and you can't run it (because compact replaces the original file). Most programmers of Access applications who encounter this problem will either tolerate the occasional failure of this kind of overnight maintenance (volume shadow copy still allows a backup of the file, though there's no guarantee that backup copy will be in a 100% internally consistent state), or they will engineer their Access applications to terminate at a time appropriate to allow overnight maintenance operations. I've done both, myself.
In non-Access applications, the same problem exists, but has to be tackled differently. For web applications, it's something of a problem, but in general, I'd say that any web app that churns the data enough that a compact would be needed is one for which a Jet/ACE data store is wholly inappropriate.
Now, on the subject of COMPACT ON CLOSE:
It should never be used by anyone.
Ever.
It's useless and downright dangerous when it actually kicks in.
It's useless because there's no properly-architected production environment in which users would ever be opening the back end -- if it's an Access app, it should be split, with users only ever opening the front end, and if it's a web app, users won't be interacting directly with the data file. So in both scenarios, nobody is ever going to trigger the COMPACT ON CLOSE, so you've wasted your time turning it on.
Secondly, even if somebody does occasionally trigger it, it's only going to work if that user is the only one with the database open. As I said above, it can't be compacted if there are other users with it open, so this isn't going to work, either -- COMPACT ON CLOSE can only run when the user triggering it has exclusive access.
But worst of all, COMPACT ON CLOSE is dangerous and if it does run can lead to actual data loss. This is because there are certain states an Jet/ACE datebase can be in wherein internal structures are out of whack, but the data is all still accessible. When the compact/repair operation is run in that state, data can potentially be lost. This is an extremely rare condition, but it is a very remote possibility.
The point is that COMPACT ON CLOSE is not conditional, and there is no prompt that asks you if you want to run it. You don't get a chance to do a backup before it runs, so if you have it turned on and it kicks in when your database is in that very rare state, you could lose data that you'd otherwise be able to recover if you did not run the compact operation.
So, in short, nobody with any understanding of Jet/ACE and compacting ever turns on COMPACT ON CLOSE.
For a single user, you can just compact as needed.
For a shared application, some kind of scheduled maintenance script is the best thing, usually running overnight on the file server. That script would make a backup of the file, then run the compact. It's quite a simple script to write in VBScript, and easily scheduled.
Last of all, if your application frequently deletes large numbers of records, in most cases that's an indication of a design error. Records that are added and deleted in regular production use are TEMPORARY DATA and don't belong in your main data file, both logically speaking and pragmatically speaking.
All of my production apps have a temp database as part of the architecture, and all temp tables are stored there. I never bother to compact the temp databases. If for some reason performance bogged down because of bloat within the temp database, I'd just copy a pristine empty copy of the temp database over top of the old one, since none of the data in there is anything other than temporary. This reduces churn and bloat in front end or back end and greatly reduces the frequency of necessary compacts on the back end data file.
On the question of how to compact, there are a number of options:
in the Access UI you can compact the currently open database (TOOLS | DATABASE UTILITIES). However, that doesn't allow you to make a backup as part of the process, and it's always a good idea to backup before compacting, just in case something goes wrong.
in the Access UI you can compact a database that is not open. This one compacts from an existing file to a new one, so when you're done you have to rename both the original and the newly compacted file (to have the new name). The FILE OPEN dialog that asks you what file to compact from does allow you to rename the file at that point, so you can do it as part of the manual process.
in code, you can use the DAO DBEngine.CompactDatabase method to do the job. This is usable from within Access VBA, or from a VBScript, or from any environment where you can use COM. You are responsible in your code for doing the backup and renaming files and so forth.
another option in code is JRO (Jet & Replication Objects), but it offers nothing in regard to compact operations that DAO doesn't already have. JRO was created as a separate library to handle Jet-specific features that were not supported in ADO itself, so if you're using ADO as your interface, the MS-recommended library for compacting would be JRO. From within Access, JRO is inappropriate for compact, as you'd already have the CompactDatabase method available, even if you don't have a DAO reference (the DBEngine is always available in Access whether or not you have a DAO reference). In other words, DBEngine.CompactDatabase can be used within Access without either a DAO or ADO reference, where as the JRO CompactDatabase method is only available with a JRO reference (or using late binding). From outside of Access, JRO may be the appropriate library.
Let me stress how important backups are. You won't need it 999 times out of 1000 (or even less often), but when you need it, you'll need it bad! So never compact without making a backup beforehand.
Finally, after any compact, it's a good idea to check the compacted file to see if there's a system table called MSysCompactErrors. This table will list any problems encountered during the compact, if there were any.
That's all I can think of regarding compact for now.
Open the mdb and do a 'Compact and Repair'. This will reduce the size of the mdb.
You can also set the 'Compact on Close' option to on (off by default).
Here is a link to some additional information:
http://www.trcb.com/computers-and-technology/data-recovery/ways-to-compact-and-repair-an-access-database-27384.htm
The Microsoft Access database engine provides a CompactDatabase method that makes a compact copy of the database file. The database file must be closed before calling CompactDatabase.
Documentation:
Pages on microsoft.com about "Compact and Repair Database"
DBEngine.CompactDatabase Method (DAO)
Here's a Python script that uses DAO to copy and compact MDB files:
import os.path
import sys
import win32com.client
# Access 97: DAO.DBEngine.35
# Access 2000/2003: DAO.DBEngine.36
# Access 2007: DAO.DBEngine.120
daoEngine = win32com.client.Dispatch('DAO.DBEngine.36')
if len(sys.argv) != 3:
print("Uses Microsoft DAO to copy the database file and compact it.")
print("Usage: %s DB_FILE FILE_TO_WRITE" % os.path.basename(sys.argv[0]))
sys.exit(2)
(src_db_path, dest_db_path) = sys.argv[1:]
print('Using database "%s", compacting to "%s"' % (src_db_path, dest_db_path))
daoEngine.CompactDatabase(src_db_path, dest_db_path)
print("Done")
With python you can compact with the pypyodbc libary (either .mdb or .accdb)
import pypyodbc
pypyodbc.win_compact_mdb('C:\\data\\database.accdb','C:\\data\\compacted.accdb')
(source)
Then you can copy compacted.accdb back to database.accdb with shutil:
import shutil
shutil.copy2('C:\\data\\compacted.accdb','C:\\data\\database.accdb')
(source)
Note: As far as I know for Access DB with ODBC, python and its libraries must be 32bit (link). Also, these steps probably only work with Windows OS.

MS Access: Avoiding "Lost Updates"

We've inherited a rather large Access DB that has some very strange (and unsettling) behaviour: Some updates to the database are lost. What can we do to avoid this? Are there any settings in Access to provide better transactional control?
Here are some more details:
We have one access user that has write access (shared by a very limited number of users - currently only me as the others are on vacation).
We have another access user that is used by many people that only has read access.
Some changes to the data are made by the write user.
After leaving and re-entering the table and/or even the application, the changes seem to have been "committed".
After a while (usually overnight) the changes are lost and the data is back to the old values.
What can cause this behavior?
Our theory is that this is caused by some weird transactional control within Access. The read-only user gets some kind of "exclusive lock" to the data that is used within a query or form. Once the user leaves that query/form, Access makes sure that this is still in the database. If in the meantime the write user has changed the data, these changes will be reverted when the read only user leaves the query/form, resulting in a lost update. Does this make sense? Is this a known issue with MS-Access??
Also, we're interested how we can avoid this issue. Is this something that is inherent in Access and can only be avoided by switching to a "real" database with better transactional control? (From a technical viewpoint, that would be nice; but of course it would be an urgent todo that we would rather avoid at this point in the project.)
Thanks for any input and let me know if there is extra information that is required.
I have seen something slightly similar more than a decade ago, before the bookmark bug was found and patched. In that case, leaving an edited record via bookmark navigation was losing the edit without throwing an error.
In that case, before MS patched it, the solution was to force the save before navigating away from the record:
With Me.RecordsetClone
.FindFirst "..."
If Not .NoMatch Then
If Me.Dirty Then
Me.Dirty = False
End If
Me.Bookmark = .Bookmark
End If
End With
The other thing to check is if error reporting is turned on and how that's interacting with the VBE error handling settings. If the code is littered with On Error Resume Next in order to ignore generated errors, then the code needs to be completely rewritten. The problem with it is that it doesn't always go out of scope as expected.
I never use On Error Resume Next, myself. Instead, if I'm expecting a particular error but want to ignore it, I trap for that error and ignore it, and thus allow any other errors that I haven't anticipated to not disappear down the memory hole.
My thinking here is that some kind of locking issue is generating an error that's being ignored. Thus, the error never gets reported and the changes get lost without anyone ever knowing.
Another thing to look at is whether the back end is stored on a file server where the drives are replicated. This is an untenable setup for Access and Jet, because it completely kills all of Jet/ACE's record locking and internal transactions because the file image is in a state of flux. I've seen reports of what you're describing where two servers were replicated, and both versions of the MDB were being edited. The result was loss of data when the file system replication kicked in and overwrote the changes on one side with the changes from the other.
Your clarification of the scenario is appreciated.
The idea that a read-only user could cause changes by a write user to be lost is not possible.
Maybe the read-only users aren't actually read-only? How, exactly is read-only vs. read/write implemented? In my apps, I'd probably have the forms by default set to read-only, and set Allowedits/Allowadditions/Allowdeletions on the forms to True for the read/write user. I assume from what you've said that you're using Jet user-level security, likely with the default Admin user being the read-only user, and some other user name being the read/write user. Thus, in all the forms you could do this in the OnLoad event:
Me.AllowEdits = (CurrentUser()<>"Admin")
Me.AllowDeletions = Me.AllowEdits
Me.AllowAdditions = Me.AllowEdits
It would also be wise to apply user-level restrictions to the tables (back end only, or both back end and front end), giving read-only Admin user group and User user group, and then give read/write to your one user. Jet ULS is not like NTFS security, where least permissive permissions win -- in Jet ULS, MOST permissive wins, and that's why you have to make sure that you make both Admin and User groups read-only (and give no permissions to the Admin user specifically, i.e., so all permissions are inherited from group membership).
I'm assuming, of course, that this is not being attempted through NTFS permissions on the back-end MDB. If so, that's likely to be a major problem and is simply not the right way to do it. From what you've said, it seems unlikely that this is what is going on, so I won't say any more about it.
But the first thing I'd look at is whether or not the so-called read-only users really are read-only.
Inherited MS Access is always the best,isn't it. To get the obvious solution over and done with early, I recommend moving the data over to SQL Server. It's really not a big job. You might also get some benefit by splitting the database (GUI and data) into separate .mdb's
That all said, I've never come across that sort of behaviour before. Obvious questions are:
How large is the database?
What version is it?
Has it been compacted and repaired recently (take a backup first of course)?
There is transaction processing that you can do with MS Access, but I've found it to be a pain in the butt, and often leads to bizarre errors (until you start grokking the way that the transactions are colliding). Most of your application is probably assuming optimistic locking, and the default behaviour is contradictory to transactions the way you would think of them in Oracle or any other real database. It's probably not worth it.
One more thought, perhaps there's two people with write access looking at the same record at the same time; and one is doing updated in a different field of the record.
Some thoughts:
Is there a genuine need for 2 people to have write access to the same table at the same time? I think you can lock out the 2nd person easily enough by changing the Record Locking and Open Modes in Tools->Options.
You said the updated data usually reverts to its old self at night. Could someone be running an overnight process that also updates the same tables?