Bob Balaban's Blog

     
    alt

    Bob Balaban

     

    Geek-o-Terica 11: View AutoUpdate, Part Deux

    Bob Balaban  April 14 2010 06:19:01 PM
    Greetings, Geeks!

    A little over a week ago I posted on the topic of the View.AutoUpdate property, and made some comments about how it affects the results and the performance of agent (or other) code accessing views. I promised a follow-up post delving a bit more deeply into this topic, and here it is.

    I created a form named "Numbers", with a single field on it, of type number, and name "number"
    I created a view (named "Numbers") with a selection formula including all the documents whose forn field is "number", and created 1 column containing the "number" field from the form. I set the column to sort, ascending.

    Then I created 5 documents using the "Numbers" form, with the values: 2, 4, 6, 8 and 10.

    Then I wrote this LotusScript agent:

    option public
    option declare             ' no kittens were harmed in the creation of this agent
    Sub Initialize
            Dim n As Integer
            Dim s As New NotesSession
            Dim db As NotesDatabase
            Dim doc As NotesDocument, tmp As NotesDocument
            Dim v As notesview
           
            Set db = s.currentdatabase
            Set v = db.getview("Numbers")
            v.AutoUpdate = True
            Set doc = v.GetFirstDocument
            While Not (doc Is Nothing)
                    n = doc.number(0)
                    Msgbox "Number is: " & n
                    If n = 4 Then
                            doc.number = 7
                            doc.save True, False
                    End If
                    Set doc = v.getnextdocument(doc)
            Wend
    End Sub

    Note that I'm setting Autoupdate to "True" (it's the default setting anyway). The agent looks at (and prints out) the number value of every document, as accessed in order from top to bottom in the view. When we get to the 2nd document (number = 4), we change the value to 7 and save the document.

    With AutoUpdate set to True, the sequence that gets printed out is: 2, 4, 8, 10. Notice that 6 got skipped, because changing the value of the second document from 4 to 7 moved that document in the sort order AFTER the third document, which contains the value 6. So the new "next" document is the containing 8, because the getNextDocument() logic caused our snapshot of the view to be updated before retrieving the next document in the sequence.

    Then I reset the value of the document now ontaining 7 to 4 again, and re-ran the agent with AutoUpdate set to False. The sequence that gets printed out this time is: 2, 4, 6, 8, 10. At the end of the agent's run, the view contains: 2, 6, 7, 8, 10, as it should. The difference is, that with AutoUpdate off, our snapshot of the view index remains constant after the change and save of the 2nd document, so the "next" one after that is STILL the one containing the 6.

    That's it, in a nutshell. One final comment: Were this a Java agent, you would change the Set doc = v.getnextdocument(doc) line to:
         tmp = v.getNextDocument(doc);
         doc.recycle();
         doc = tmp;

    Geek ya later!

    (Need expert application development architecture/coding help? Contact me at: bbalaban, gmail.com)
    Follow me on Twitter @LooseleafLLC
    This article ┬ęCopyright 2010 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.
    Comments

    1Nathan T. Freeman  4/14/2010 11:45:10 AM  A more interesting test...

    Sub Initialize

    Dim v As NotesView

    Dim db As NotesDatabase

    Dim s As New NotesSession

    Dim entry As NotesViewEntry

    Dim coll As NotesViewEntryCollection

    Dim n As Integer

    Set db = s.currentdatabase

    Set v = db.getview("Numbers")

    v.AutoUpdate = False

    Set coll = v.Allentries

    Set entry = coll.getFirstEntry()

    While Not (entry Is Nothing)

    If entry.Document.number(0) = 4 Then

    entry.Document.number = 7

    entry.Document.save True, False

    End If

    MsgBox "Number is: " & n

    n = CInt(entry.Columnvalues(0))

    Set entry = coll.getNextEntry(entry)

    Wend

    End Sub

    2Erik Brooks  4/14/2010 2:11:23 PM  Geek-o-Terica 11: View AutoUpdate, Part Deux

    @1 - I think you'll need to set the entry.document to a variable and reference that or else it won't save your field change properly.

    @Bob - Good intro to unravelling the first mysteries of AutoUpdate. :-) I'd be curious to see these test repeated (a) against a large view and (b) with a different thread writing to that one doc.

    For example:

    Case #1: if you changed the 4 to something well forward of the current cached view page (e.g. 92384) then I have a feeling you'd end up processing the doc twice even with AutoUpdate=False.

    Case #2: If another thread inserted a new doc forward of the current doc but still in the page currently cached then that new doc could potentially be skipped. The apparent behavior would seem very inconsistent. That other thread could be the Update task, or another agent calling NotesView.Refresh.

    With AutoUpdate=True then neither case should ever occur. Though in Case #1 you would skip an entire batch of docs completely, just as how doc 6 was skipped in your example.

    3Nathan T. Freeman  4/14/2010 3:05:46 PM  Geek-o-Terica 11: View AutoUpdate, Part Deux

    @2 meh,I didn't do so much as a compile test, so sure, it might need tweaking. The operative question is: does the SummaryItemTable reflect an update when AutoUpdate=False? Forget the collection sequence -- since real men use Entries, the issue is whether the data values reflect updates.

    4Erik Brooks  4/14/2010 10:19:13 PM  Geek-o-Terica 11: View AutoUpdate, Part Deux

    LMAO "real men use Entries" - that needs to be on a t-shirt.

    If you're using AutoUpdate=False then I'd wager that any entry changes made to docs within the currently-cached view page will not be reflected in your loop. But any entry changes made on a page in the view not yet in cache will be reflected by your loop if another thread updates the view before your code gets there.

    If you're using AutoUpdate=True then all entry changes should be reflected, all the time.

    The *really* fun thing is: if you're using AutoUpdate=False and a doc within your currently-cached page is modified then your entries will not reflect the change. But entry.document *would*.

    Basically AutoUpdate=False says "I kinda sorta don't want my code to be affected by updates." But it can't guarantee it.

    AutoUpdate=True at least ensures consistent behavior (latest-changes always.)

    5Bob Balaban  4/15/2010 2:09:06 AM  Geek-o-Terica 11: View AutoUpdate, Part Deux

    @1 - Thanks for the feedback, Nathan. Did you try running this program? I did, here's what I saw: 0, 2, 4, 6, 8

    Of course, 0 is not one of the numbers in the view. Not really sure what you were getting at. Something to do with whether doc.columnvalues changes when the document moves in the view? I think that would actually be a slightly different test. My hypothesis (haven't tried it yet) is that it would track properly, regardless of the AutoUpdate setting, because the columnvalues are retrieved in the same NIFReadEntries() call that does the view navigation.

    6Nathan T. Freeman  4/15/2010 3:23:29 AM  Geek-o-Terica 11: View AutoUpdate, Part Deux

    @5 - I didn't try running it, no. And I see I got the MsgBox out of order from the assignment for n, so that'll never work right. :-) Feel free to fix my crappy code. The 0 is because that's the default value for the integer.

    That being said, if you got any message box at all containing 4, then AutoUpdate=False didn't just suppress the sequencing update, but also the summary item update. Because we changed the data in the document and saved it BEFORE requesting the columnValues, yet you still got 4 instead of 7.

    The implication is that with AutoUpdate=False, we really are getting a snapshot of each page of the index. Regardless of whether that's good or bad, it's certainly important to know that it IS.

    @4 - I think you might be rationalizing a bit here on not wanting to go put AutoUpdate=False in all your code, man. :-) If it really doesn't call thread spins, and you know that you're getting a point-in-time snapshot for each page through the index, then you know your behavior. Honestly, how long does it take to walk a single page of an index if you're algorithm is decent on modern hardware?

    7Erik Brooks  4/15/2010 5:29:03 AM  Geek-o-Terica 11: View AutoUpdate, Part Deux

    @6 - Oh, I am to an extent. :-) I've got various bits using True and others using False depending on the desired behavior.

    It's a very subtle distinction to have a piece of the index frozen versus the entire index itself. For a single-threaded environment or a sufficiently small view (like this "Numbers" example) they're one and the same. But in particular if you're working with a very large view in a busy db then you've got potential concerns. AutoUpdate=False absolutely will run faster than AutoUpdate=True in many cases (though the new fix coming out makes AutoUpdate=True much closer under load.) But it's important to note that it's not a magical panacea that lets you freeze the view index.

    I wish it could. The only REAL way to freeze an index is to set your view's refresh to "Manual" and specifically call a NotesView.Refresh() prior to iterating through it, knowing that only your thread will be calling the Refresh().

    Even then you may still need to check each doc since the ColumnValues() may not be accurate anymore. You've also got to check Document.isValid, isDeleted, etc. and if you're being extra-careful then you should check the selection formula also since it might be the case that the doc shouldn't even be there anymore.

    You've got all of the same concerns with AutoUpdate=False.

    So yeah, generally I don't want to use AutoUpdate=False. The selection formula is the deal-breaker for me. If a doc got pulled out of the view by another thread just before my agent touches it -- even if it happen one second ago -- I usually don't want it processed. So I stick with AutoUpdate=True.

    It really depends on your needs and tolerance to erroneous processing.