Search Search

#1 worldwide
FREE Coding Lessons

since 1996
   THE BEST WAY to learn ASP & Asp.net!
Advertise Here!
click for details
Credits Host:
DiscountASP.net
Server Admin:
The "Team"
Contact Info.
Charles M. Carroll

my Blog
[prev. Lesson]  DB: Deleting a Record w/SQL
     [next Lesson]  Access Tips: Passwords, RecordCount

Access Scalability Notes by Charles Carroll

This page demonstrates how many Access connections can be opened by one script. Testing generally indicates a single script can only initiate 64 connections. Microsoft Tech Support has suggested a 255 limit easily disproved by this script. As a point of reference I ran this same script against a SQL7 data source and attempted 2,550 connections. All were sucessful.

Access has the following limitations:

1. Access is STA (single-threaded-apartment), NOT MTA or Free-Threaded (see http://www.learnasp.com/advice/threads.asp for thread basics). What this means is that all database requests are serialized, i.e.
user1 asks for 100 records
user2 asks for 2 records
user3 asks for 5000 records
user4 asks for 3 records
If an agile back-end (free-threaded or MTA for example SQLserver, Oracle, etc.) was handling those requests it could send user2 and user4 records before dealing with other requests. Access cannot. User 4 will receive their data AFTER the other requests have completed. Requests are handled as they come in and to completion before next low-level data access starts. So the application's pattern of retrieval makes a huge difference in whether an Access app will scale.

2. Since open connections are the issue and scripts run round-robin, see: http://www.learnasp.com/advice/roundrobin.asp  (see advice about open late/close early) http://www.learnasp.com/advice/whybuffer.asp  http://www.learnasp.com/advice/whygetrows.asp  are EXTREMELY relevant in this case.

3. Access unfortunately does not support connection pooling which can dramatically improve performance, see:
http://www.learnasp.com/freebook/asp/dbpooling.aspx

4. The free answer to this problem is MSDE, a chopped down version of SQLserver that Microsoft gives away which can handle much more load, see:
http://msdn.microsoft.com/vstudio/msde/

Additional Note: ODBC connections are very flaky producing a variety of run-time error messages that OLEDB connections do not.  This sample uses an OLEDB connection hence it's "microsoft.jet.oledb...." vs. a connection string that contains "{microsoft access...}". The latter connection string/ODBC connection is far less reliable. See:
http://www.learnasp.com/freebook/asp/dbopen.aspx for more connection string info.
For example: [Microsoft][ODBC Microsoft Access Driver]General error Unable to open registry key 'Temporary (volatile) Jet DSN for process 0xda8 Thread 0x49c DBC 0x254d024 Jet'. occurs when connecting to data with "{microsoft access..." connection string whereas exactly the same data can be accessed with "Jet.oledb" string fine.

   filename=/learn/test/accesstest.asp

<Test Script Below>


<HTML><HEAD><TITLE>accesstest.asp</TITLE></HEAD>
<body bgcolor="#FFFFFF">
<%
on error resume next
myconn = "PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" & server.mappath("nwind.mdb") & ";"
' the above string is recommended way to connect
' myconn = "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=" & server.mappath("nwind.mdb") & ";"
' the above connect string is VERY flaky
dim test(256)
dim testRS(256)
dim errn(256)
dim errd(256)
dim errrs(256)
maxTests=256
rsErrorCount=0
response.write "<hr>Staring Access Scalability Test<br>"
response.flush
debugThisScript=False

for counter=1 to maxtests
    set test(counter)=server.createobject("adodb.connection")
    set conntemp=server.createobject("adodb.connection")
    set test(counter)=conntemp
    IF debugThisScript=True THEN
        response.write "cn-"
        response.flush
    END IF
    goodconn=true
    goodRS=true

    vbsError=0
    errorcount=0
    conntemp.open myconn
    ' have to test error here because if conn cant be created
    ' the errcount... line will fail
    vbserror=err.number
    errcount=conntemp.errors.count
    IF errcount>0 then
        DBErrNum=conntemp.errors(0).number
        DBErrDes=conntemp.errors(0).description
        errn(counter)=DBErrNum
        errd(counter)="ADO: " & DBErrDes
        goodconn=false
        IF debugThisScript=True THEN
            response.write "cADOErr-"
            response.flush
        END IF
    ELSE
        IF debugThisScript=True THEN
               response.write "op-"
            response.flush
        END IF
    END IF

    ' VBScript Error Trap
    IF vbserror<>0 THEN
        errn(counter)=vbserror
        errd(counter)="VBS: " & err.description
        goodconn=false
        IF debugThisScript=True THEN
            response.write "cVBSErr-"
            response.flush
        END IF
    END IF

    If goodconn=true THEN
        ConnCounter=ConnCounter+1
        errn(counter)=0
        success=success+1
        vbserror=0
        errcount=0
        set tempRS=server.createobject("adodb.recordset")
        set tempRS=conntemp.execute("select categoryID,CategoryName,Description from Categories")
        ' only a dozen records or less
        vbserror=err.number
        errcount=conntemp.errors.count
        IF vbserror<>0 THEN
            errRS(counter)="VBS Recordset Error: " & err.description & "<br>"
            goodRS=false
            IF debugThisScript=True THEN
                response.write "rVBSErr-"
                response.flush
            END IF
        ELSE
            'errRS(counter)=""
        END IF

        ' check for ADO error
        ADOErrorCount=conntemp.errors.count
        tenpADOerr=""
        IF ADOErrorCount>0 THEN
           response.write "rADOErr-"
           response.flush
           rsErrorCount=rsErrorCount+1
           for counterADO=0 to ADOErrorCount-1
              ADOerrornum=conntemp.errors(counterADO).number
              ADOerrordesc=conntemp.errors(counterADO).description
              tempADOerr=tempADOerr & "#" & ADOErrornum & " =>" & ADOErrorDesc
               next
            errRS(counter)=errRS(counter) & tempADOerr
            goodRS=false
        ELSE
            IF debugThisScript=True THEN
                    response.write "ro-<br>Data Start:<br>"
                    response.flush
            END IF
            errRS(counter)=""
            DO WHILE not tempRS.EOF
                IF debugThisScript=True THEN
                    response.write tempRS(0) & ","
                    response.write tempRS(1) & ","
                    response.write tempRS(2) & "<br>"
                END IF
                Cellcounter=CellCounter+1
                tempRS.movenext
            LOOP
            TempRS.Close
            IF debugThisScript=True THEN
                response.write "Data End<hr>"
            END IF
            response.flush
            RSCounter=RSCounter+1
        END IF

    else
        'exit for
        'response.write "<br>"
    END IF

    IF response.isclientconnected()=false THEN
        exit for
    ELSE

    end if

    'conntemp.close()
    'set conntemp=null

    'response.write " *** "
    'response.flush
NEXT
response.write "<hr>Closing connections and recordsets now<br>"
set RsTemp=nothing
for counter=1 to maxtests
    test(counter).close
    set test(counter)=nothing
       testRS(counter).close
       set testRS(counter)=nothing
next
response.write "<hr>Succeeded creating " & Conncounter & " Connections<br>"
response.write "<hr>Succeeded creating " & RScounter & " Recordsets<br>"
response.write "<hr>" & CellCounter & " Cells (rows x columns) Fetched<br>"
response.write "<hr>Closed all connection/recordsets<br>"

' Print Out Errors
errorcountCon=0
errorcountRS=0
for counter = 1 To maxtests
    errnum=errn(counter)
    IF errnum<>0 THEN
        tempError=errd(counter)
        If tempError="VBS: Unspecified error" THEN
            unspecifiedErrCounter=unspecifiedErrCounter+1
        Else
            response.write "#" & counter & " connections failed:"
            response.write TempError  & "<br>"
        END IF
        errorcountCon=errorcounterCon+1
    END IF
    errRSDescription=errRS(counter)
    If errRSDescription<>"" THEN
            response.write "#" & counter & " recordset had error!"
            response.write errRSDescription & "<br>"
            errorcountRS=errorcountRS+1
    END IF
    If errorcoount=1 THEN
        response.write ("<hr>Error List<br>")
    END IF
next

If errorcountCon=0 THEN
        response.write ("<hr>No Connection Errors!<br>")
end if
If errorcountRS=0 THEN
        response.write ("<hr>No Recordset Errors!<br>")
end if
If unspecifiedErrCounter>0 THEN
    response.write ("<hr>" & unspecifiedErrCounter & " Unspecified Errors opening connections!<br>")
END IF
%>
</BODY></HTML>

There are many worthy charities!!. But perhaps help starving children in Africa or South America AND help Charles too. a $5 tip buys him lunch at McDonalds, a $20 tip buys his kid Hitoshi a new computer game, a $39 tip buys his daughter Michiko a few nice outfits. See our donor list.