By Dr. Peter Wayne
A number of users have experienced "heap lock errors" in Alpha Five version 4. When this happens a working application will suddenly halt with a message that looks like this:

This error message means that Alpha Five has tripped over itself in accessing your computer's memory. In a multitasking operating system like Windows, memory that is not specifically used by any running program is part of the "heap." When a program such as Alpha Five finds it is running out of memory, it grabs a block of unused memory from the heap, and places a "lock" on the memory it has claimed. When it is finished with the memory, it should return the block of memory to the heap for other programs to use.
Heap lock errors can occur if your application makes strenuous demands on Alpha Five's indexing system. As far as I have been able to ascertain, there are 2 conditions necessary for a heap lock error:
For example, in this sample table, there are 3 indexes:

2 of the indexes have the same filter: .not. marked() If my users do a lot of marking and unmarking of records, then I can run into a heap lock error as Alpha Five has to continually update the filtered indexes.
I was able to produce a heap lock error in a test table with this script:
plm=table.open("pl_main",file_rw_exclusive)
plm.update_production_index()
nrecs=plm.records_get()
' first, unmark all records
i=0
plm.fetch_first()
while .not. plm.fetch_eof()
i=i+1
statusbar.percent(i,nrecs)
plm.change_begin()
plm.unmark()
plm.change_end(.t.)
plm.fetch_next()
end while
plm.close()
' now, mark them
plm=table.open("pl_main",file_rw_exclusive)
nrecs=plm.records_get()
i=0
plm.fetch_first()
while .not. plm.fetch_eof()
i=i+1
statusbar.percent(i,nrecs)
plm.change_begin()
plm.mark()
plm.change_end(.t.)
plm.fetch_next()
end while
plm.close()
Script 1. Script to produce a heap lock error in a test file.
The heap lock error occurs in the second part of the script, as the records are sequentially marked. In the particular test file I used, the heap lock error occurred after about 3,900 of 10,000-odd records were marked.
3,900 operations may seem like a lot of operations, but depending on the number of users on the system, the number of indexes in the table, the number of filters in the indexes, and possibly the number of records in the table, errors may occur with far fewer markings. I recently looked at an otherwise superbly designed application that was experiencing heap lock errors on a daily basis. The main table of the application had 27 indexes, of which 12 were filtered! It wasn't even clear why there were so many indexes - some may have been left over from development of the system. The application developer was able to remove 10 of the filtered indexes without any adverse impact on the application.
It is rare that you actually need a filtered index. If you recall that you can place a filter in the detail section of a report and in the properties of a browse or form, you will realize that you don't need to place a filter in the index.
There is some good news here, too. Just in case you can't cure yourself of filtered indexes, I should tell you that it is much harder to produce a heap lock error in A5v5. Not impossible, mind you - but you have to work at it. Just 2 or 3 indexes with the same filter won't do - you need to have at least 5, in my testing, and even then it's not easy to bring v5 to its knees.
If you want to play with heap lock errors, try running this script in v4 and in the beta versions of v5. It will generally produce an error in v4 after about 9000 markings but will usually cause no trouble in v5:
constant max_records=15000
dir_put(a5.get_path())
if .not. file.exists("heaptest.dbf") then
table.create_begin("name","C",6)
table.field_add("name2","C",6)
table.field_add("date","D",8)
table.field_add("number","N",6,0)
tbl=table.create_end("heaptest.dbf")
file_add_to_db("heaptest.dbf")
Table.index_create_begin("name","name",".not. marked()")
Table.index_add("name2","name2",".not. marked()")
Table.index_add("number","number",".not. marked()")
table.index_add("date","date",".not. marked()")
table.index_add("name_name","name+name2",".not. marked()")
tbl.index_create_end()
for i=1 to max_records
statusbar.percent(i,max_records)
tbl.enter_begin()
let=chr(64+ceiling(26*rand()))
tbl.name=let+alltrim(str(ceiling(1000*rand())))
tbl.name2=let+alltrim(str(ceiling(1000*rand())))
tbl.date={1/1/1990}+ceiling(max_records*rand())
tbl.number=max_records*ceiling(rand())
tbl.enter_end(.t.)
next
tbl.close()
end if
t=table.open("heaptest")
t.update_production_index()
t.index_primary_put()
t.fetch_first()
i=0
while .not. t.fetch_eof()
i=i+1
statusbar.set_text("Unmarking record "+alltrim(str(i)))
t.change_begin()
t.unmark()
t.change_end(.t.)
t.fetch_next()
end while
t.close()
t=table.open("heaptest")
t.index_primary_put()
t.fetch_first()
i=0
while .not. t.fetch_eof()
i=i+1
statusbar.set_text("Marking "+alltrim(str(i)))
t.change_begin()
t.mark()
t.change_end(.t.)
t.fetch_next()
end while
t.close()
Script 2.
| Database consultant Ira Perlow
writes: An index filter is completely different from a filter in other places. While I don't use them often (I actually use the technique described later), I actually believe that they are an important component of an efficient database application in many cases. The filter in the index is evaluated at record save time, and the record's
key is either placed in the index or not. Other filters (and queries) are
evaluated at the time of use. Thus, in the latter case, you are evaluating the
filters each and every time you access a record. This puts a much higher load
on the network and database being accessed. On the other hand, there is a
simple solution to the filtered index... The trick is to place filtered and
non-filtered records into different parts of the index keys. Thus for your
example (assuming character fields), the index expression would be: This places a "1" before all keys that are marked and a "0" for the rest. Now just set ranges as needed and do finds with "0"'s before the rest of the key! |
6/18/00
Don't forget, we need your feedback to make this site better!