views:

631

answers:

3

In my application I do the follwing things:

  1. Open a Access database (.mdb) using Jet/ADO and VB6
  2. Clear and re-fill a table with new data
  3. Close the database
  4. Start another process which does something with the new data.

The problem is that sometimes the second process cannot find the new data. Sometimes the table is just empty, sometimes RecordCount > 0, but EOF is true and I cannot do a MoveFirst or MoveNext. In a nutshell: All kinds of weird things.

My current workaround is adding a delay between closing the database and starting the second process.

  • What is happening here?
  • Can I do something about it? (Besides using a different database)
A: 

Since the first process is the only process with the MDB open it can be a bit lazy about writing stuff back to the file. Even after the you end the process there can be a delay whilst the OS writes back the outstanding pages and this can happen after the process has signalled that it has completed.

My recommendation would be, stop using Access, use SQL Server 2008 Express instead.

AnthonyWJones
-1 It's terrible advice in this case, and almost never helpful in any other instance.
David-W-Fenton
Terrible? how so?
AnthonyWJones
+6  A: 

Just a guess but I might be due to the fact that the Jet engine features a read cache and lazy writes:

How To Implement Multiuser Custom Counters in Jet 4.0 and ADO 2.1

"Microsoft Jet has a read-cache that is updated every PageTimeout milliseconds (default is 5000ms = 5 seconds). It also has a lazy-write mechanism that operates on a separate thread to main processing and thus writes changes to disk asynchronously. These two mechanisms help boost performance, but in certain situations that require high concurrency, they may create problems."

The article suggests using Jet's RefreshCache method and to set the Jet OLEDB:Transaction Commit Mode to 1 millisecond (one advantage for ADO over DAO for Jet is that you can alter this setting without changing the value in the registry).

P.S. you should consider editing Access database (.mdb) to mention 'Jet' instead and using the 'Jet' tag too, otherwise you'll get a comment from a certain SO user who is pernickety about these things :)

onedaywhen
Which user is it?
DR
mmhhh ..... I guess I know this guy .... Onedaywhen is right: be careful or you'll get whipped ...
Philippe Grondier
You might be setting up a race condition with the 1 millisecond setting? Call me paranoid (and immodest!) but IMHO my answer is better.
MarkJ
I meant JET. JET was good in its day. I've done a lot of VB6/JET work but VB6/SQL Express works equally well especially now you can define dynamic attachment in the connection string.
AnthonyWJones
+1 for voting up my answer :)
MarkJ
+2  A: 

This Microsoft Knowledge Base article explains how to do it.

Here's an excerpt with sample code. The code uses two connections from one process, so you need to pull the reading part into your second process.

  1. The writer must start a transaction, using ADO's Connection.BeginTrans, prior to writing the data.
  2. The writer must make the database updates and then commit the transaction (using ADO's Connection.CommitTrans).
  3. The reader must call JRO.JetEngine.RefreshCache passing in it's connection prior to attempting to read the data.

Note that JRO.JetEngine is included by adding a reference to the Microsoft Jet And Replication Objects 2.1 Library to your VB project.

    Sub SyncReadDemo()
    Dim conn1 As New ADODB.Connection
    Dim conn2 As New ADODB.Connection
    Dim rs As New ADODB.recordset
    Dim JRO As New JRO.JetEngine
    Dim strConnect As String
    Dim i As Long


  ' Set up our connection string (requires a database named c:\db1.mdb).
    strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\db1.mdb"

    ' Open connection 1 and drop and re-create test table.
    conn1.CursorLocation = adUseServer
    conn1.Open strConnect
    On Error Resume Next
    conn1.Execute "drop table tmpTest", , _
        adExecuteNoRecords + adCmdText
    On Error GoTo 0
    conn1.Execute "create table tmpTest (id long)", , _
        adExecuteNoRecords + adCmdText

    ' Close connection 1 to flush the creation of table tmpTest. 
    conn1.Close

    ' Now open connection 1 and connection 2.
    conn1.Open strConnect
    conn2.Open strConnect

    ' Insert 10 records using connection 1.
    ' Note we must perform all writes inside of a transaction.
    conn1.BeginTrans
    For i = 1 To 10
        conn1.Execute "insert into tmpTest (id) values (1)", , _
            adExecuteNoRecords + adCmdText
    Next i
    conn1.CommitTrans

    ' Refresh cache for reader connection.
    JRO.RefreshCache conn2
    Set rs = conn2.Execute("select * from tmpTest", , adCmdText)

    ' Count records in our table (should be 10).
    i = 0
    While Not rs.EOF
        i = i + 1
        rs.MoveNext
    Wend
    rs.Close

    MsgBox "Read " & i & " records using different connections."

    conn1.Close
    conn2.Close

End Sub
MarkJ
+1 for bragging that your link to a MS article it better than mine :)
onedaywhen