Jump to content

llGetNotecardLineSync()


Cain Maven
 Share

Recommended Posts

13 minutes ago, Wulfie Reanimator said:

I can't imagine there's any chance a notecard could leave cache during a simple loop-read. If there is, the safe-guards we'd have to script around the loop would be a deal-breaker for using it.

Someone remind me to ask during the next meeting on Tuesday.

Sadly, I can imagine it... but it would be great to get clarity on this. I would also like a clear answer on which functions actually force the notecard to be cached.

Link to comment
Share on other sites

44 minutes ago, Wulfie Reanimator said:

I can't imagine there's any chance a notecard could leave cache during a simple loop-read. If there is, the safe-guards we'd have to script around the loop would be a deal-breaker for using it.

Why?  The chances are rather low, for sure, but scripts get paused every bunch of iterations through a loop, especially if they're spending time actually processing the lines they're reading — which gives ample opportunity for the notecard to get unloaded.  Unless they're using an LRU caching scheme (and perhaps even then), it will happen sooner or later.  As they say, in an infinite universe, everything happens at least once, somewhere.

And, the example presented above by Frionil Fang should handle it just fine, especially with the tweak I mentioned for anyone actually wanting to use that approach.  And the natural pattern for the dataserver event version is just as simple — basically just wrapping it in a do-while statement.

But yes, encouraging one of you to ask in the next meeting was kinda the intent behind my "would be nice to know" earlier…  😁  (I'd have asked myself already, if I could actually get to the darned meetings…  they're smack in the middle of when I'm typically sleeping, so if I'm not fast asleep already, then I'm struggling to not fall asleep on the keyboard.)

Edited by Bleuhazenfurfle
Link to comment
Share on other sites

13 minutes ago, Bleuhazenfurfle said:

Why?  The chances are rather low, for sure, but scripts get paused every bunch of iterations through a loop, especially if they're spending time actually processing the lines they're reading — which gives ample opportunity for the notecard to get unloaded.  Unless they're using an LRU caching scheme (and perhaps even then), it will happen sooner or later.  As they say, in an infinite universe, everything happens at least once, somewhere.

Emphasis on simple loop-read. I'd like to think that notecards are cached for longer than a few seconds -- at least 30 or 60 seconds.

If they're cached for less than a second (and not "refreshed" by sync reads), to the point where an interrupted loop is enough to cause reads to fail, that would be a really unfortunate reality.

Edited by Wulfie Reanimator
  • Like 1
Link to comment
Share on other sites

24 minutes ago, Wulfie Reanimator said:

Emphasis on simple loop-read. I'd like to think that notecards are cached for longer than a few seconds -- at least 30 or 60 seconds.

If they're cached for less than a second (and not "refreshed" by sync reads), to the point where an interrupted loop is enough to cause reads to fail, that would be a really unfortunate reality.

My point was mostly, what happens if it gets cached for 60 seconds, but you last just happened to access that notecard around 59.9 seconds ago?  And, if they're using a FIFO caching scheme (actually more common than I'd ever assumed), then everything gets shoved out eventually, and a brief pause in your script could allow another to receive a new resource which shoves yours out.

Getting confirmation that they're using LRU, would drastically shift those odds in favour of not having to check.  But even then, caching mechanisms are the kind of thing they'd want to be able to change without asking us first.  I think I'm going to be scripting like it's a FIFO cache with ~1s churn— especially when it's so easy to do.

Edited by Bleuhazenfurfle
Link to comment
Share on other sites

Just now, Bleuhazenfurfle said:

My point was mostly, what happens if it gets cached for 60 seconds, but you last just happened to access that notecard around 59.9 seconds ago?  And, if they're using a FIFO caching scheme (actually more common than I'd ever assumed), then everything gets shoved out eventually, and a brief pause in your script could allow another to receive a new resource which shoves yours out.  (Getting confirmation that they're using LRU, would drastically shift those odds in favour of not having to check, though.)

I'm okay with those cases existing. The only thing I'd worry about is the most common one -- sim is doing operating normally, no major lag, cache isn't flooded, and the script is just looping the notecard into memory. If that's where caching can catch you out, that'd be bad.

  • Like 1
Link to comment
Share on other sites

20 minutes ago, Wulfie Reanimator said:

I'm okay with those cases existing. The only thing I'd worry about is the most common one -- sim is doing operating normally, no major lag, cache isn't flooded, and the script is just looping the notecard into memory. If that's where caching can catch you out, that'd be bad.

With the method I've been saying (and Frionil Fang demonstrated just before EDIT: actually, not quite), worst case, like, even if the caching fails completely somehow and it never finds the notecard in the cache (like they mess up an update and it's looking for an uppercase key instead of a lowercase key <insert innocent whistling>), it just degrades to what we've known and loved since forever.  There's no bad, just a little sad.  The bad only kicks in if you start making assumptions that the lines will be there when you want them to be — and that will always come back to bite you sooner or later, because you're not in charge of what caching scheme LL chose to apply.

Edited by Bleuhazenfurfle
Link to comment
Share on other sites

4 hours ago, Bleuhazenfurfle said:

we don't want to contribute to the problem, so slow down drastically until it clears

For actual use you should probably try to ensure the asset exists in the first place etc., but it's just about the same as with async reading.  I wasn't bothering with that just to sate my curiosity so I just made it give up instead of throttling (beyond the 0.1 built in delay on llGetNumberOfNotecardLines -- on every new uncached notecard the data was available on the first retry).

But yes, the most reliable way to ensure the existence would be to successfully get to the dataserver event, at least then you can be sure there's something there.

Edited by Frionil Fang
Link to comment
Share on other sites

I must be missing something here.

If we call llGetNotecardLine(), the back end has to a) check if the notecard is in the cache, b) if not, fetch and cache it, and c) retrieve and return the requested line.

Why can't llGetNotecardLineSync() do the same thing? If the notecard already is in the cache, great -- we get the speed advantage. If not, we're not going to be worse off than with the async flavor.

 

Link to comment
Share on other sites

2 minutes ago, Cain Maven said:

Why can't llGetNotecardLineSync() do the same thing?

Because llGetNotecardLine is non-blocking. When the notecard can't be fetched for a very long time (say, until sim restart), the script just doesn't get a dataserver event with the notecard line, but it's not dead in the water; it can still do other things, like timeout and report an error if nothing else.

Link to comment
Share on other sites

3 minutes ago, Cain Maven said:

I must be missing something here.

If we call llGetNotecardLine(), the back end has to a) check if the notecard is in the cache, b) if not, fetch and cache it, and c) retrieve and return the requested line.

Why can't llGetNotecardLineSync() do the same thing? If the notecard already is in the cache, great -- we get the speed advantage. If not, we're not going to be worse off than with the async flavor.

Because the function has to return immediately. If the notecard isn't in the sim's cache, the sim needs to fetch the asset first, which takes time and why llGetNotecardLine uses the dataserver event.

  • Like 1
Link to comment
Share on other sites

2 minutes ago, Qie Niangao said:

Because llGetNotecardLine is non-blocking. When the notecard can't be fetched for a very long time (say, until sim restart), the script just doesn't get a dataserver event with the notecard line, but it's not dead in the water; it can still do other things, like timeout and report an error if nothing else.

Understood -- but in those rare cases an error (say NAK, heh) could be returned. This would be no different from other synchronous functions, and handling that edge case would probably not be any harder or more complex than the timout and error reporting you suggest; it may even be simpler.

Link to comment
Share on other sites

An exception() event has a certain appeal, but I'm sure I'll get over it.

I'm missing the point of falling back to llGetNotecardLine (except to trigger caching). If the notecard isn't cached that's not going to generate events, and once it is cached, just read the line synchronously with the same NAK-catching from which we'd be falling-back.

That's why I like @Frionil Fang's approach of eschewing dataserver() altogether and recovering from NAK local where -Sync is called. Agreed that in practice it should back off the retry timing. And I'm trying not to write the blocking one-line-fetching function for this, lest reading each notecard line incur the extra overhead of a user-defined function call.

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

I'm hoping that the initial async call guarantees the NC is cached for subsequent sync calls. Like..why wouldn't it be?

Unless a sim restart occurs in the middle of reading..or someone changes the NC in the middle of reading..those are both edge cases that would result in unpredictable results anyway.

* ducks *

  • Like 2
Link to comment
Share on other sites

I just realized - the way I retooled my NC reader (in anticipation of this new feature), I only have to make one tiny change to each read, if I ever get a "NAK" response.

It's just lucky that I coded it the "Current Code" shown, so it is easy to add the "fallback" call in the "Needed Code" shown. 

The code only gets the NC line in about 3 places, so this was the easiest way without larger rewrites.

Current Code:

if (bSync) {	// Synchronous Read mode
    text = llGetNotecardLineSync(NoteName, NoteLine);
}
else {	// Asynchronous Read mode
    handle = llGetNotecardLine(NoteName, NoteLine);
    return;
}

Needed Code:

if (bSync) {	// Synchronous Read mode
    text = llGetNotecardLineSync(NoteName, NoteLine);
  	if (text==NAK) {	// Not cached, read asynchronously
	    handle = llGetNotecardLine(NoteName, NoteLine);
	    return;
    }
}
else {	// Asynchronous Read mode
    handle = llGetNotecardLine(NoteName, NoteLine);
    return;
}

 

Link to comment
Share on other sites

The good news is that notecards seem to stay in cache for over 80 minutes when untouched.

The bad news is that I can't easily test something funny like caching thousands of notecards (to see if I can force a notecard to get uncached) since the beta grid is pretty inactive. Any clever ideas for something like that? I tested the function in Leafeon.

Edited by Wulfie Reanimator
  • Thanks 3
Link to comment
Share on other sites

1 hour ago, Wulfie Reanimator said:

The bad news is that I can't easily test something funny like caching thousands of notecards (to see if I can force a notecard to get uncached) since the beta grid is pretty inactive. Any clever ideas for something like that?

I'm too lazy to do tests, but editing a notecard should effectively simulate uncaching it if you read it by name? Put an unneccessary sleep in the read loop to give yourself time to do the edit while the loop is still running.

Edited by Quistess Alpha
  • Like 1
Link to comment
Share on other sites

24 minutes ago, Quistess Alpha said:

I'm too lazy to do tests, but editing a notecard should effectively simulate uncaching it if you read it by name? Put an unneccessary sleep in the read loop to give yourself time to do the edit while the loop is still running.

The point of the test would be to figure out what (external factors) it takes for a specific notecard to get uncached. Time is very clearly not a factor we need to worry about, so I wonder if more cached notecards would push off the one that is currently cached. Worst case, I'll just wait for Tuesday. 😋

Link to comment
Share on other sites

15 minutes ago, Wulfie Reanimator said:

The point of the test would be to figure out what (external factors) it takes for a specific notecard to get uncached. Time is very clearly not a factor we need to worry about, so I wonder if more cached notecards would push off the one that is currently cached. Worst case, I'll just wait for Tuesday. 😋

These uncertainties are why I'm going with the approach I showed above. If sync fails because of cache, drop down to async!

Link to comment
Share on other sites

This was my test for the new function.

    touch_start(integer total_number)
    {
        string result = llGetNotecardLineSync(ncName, lineNo);
        if (result == NAK) {
            llOwnerSay("notecard not ready");
            llGetNotecardLine(ncName, lineNo);
        } else if (result == EOF) {
            llOwnerSay("No more lines, restting");
            lineNo = 0;
        } else {
            llOwnerSay(result);
            ++lineNo;
        }
        
    }

Granted this is in a manual read mode, once per touch, but the fall back did work.  I never used or defined the dataserver event itself. Just called a function that would invoke it. then a retry to the Sync command on the next touch_start.

I do know that on a looping read function I might should drop a simple pause in place to give the faux dataserver call a chance to do it's thing.

and lastly I also know that I should have a try counter in place and if 2 or 3 calls to the Sync version after a dataserver nudge all return NAK to fail on out and let whomever know, or to wait and try again later.

===

and one point: I crashed out of the beta server (my fault, not SL), but when I decided to get back in (it was a bit but I'm sure it was less than 30 minutes), the notecard had fallen out of the cache. Another wait and after 15 minutes it fell out of cache, but then 45 minutes later the NC was still available on first try, so de-caching through time is....???

  Additional tests:

  • I tried editing the NC and tried and got the "not ready error". 
  • I edited again (added a space, removed the space) saved.  No error
  • I edited again and added a line - got the error
  • I edited again, added a space.  - got the error
  • edited again, removed a space, no error.
  • removed another space, got the error.

Editing the card does appear to have an effect on it's caching maybe, but it also appears to be inconsistent. As is just time since it was cached. So, test for success do a blind data server nudge it if needed.

 

 

 

 

 

  • Like 1
  • Thanks 2
Link to comment
Share on other sites

38 minutes ago, Anna Salyx said:
  • I tried editing the NC and tried and got the "not ready error". 
  • I edited again (added a space, removed the space) saved.  No error
  • I edited again and added a line - got the error
  • I edited again, added a space.  - got the error
  • edited again, removed a space, no error.
  • removed another space, got the error.

When you edit a notecard, it gets a whole new UUID, but much like with uploading an identical duplicate of a texture getting the same UUID, if you save an identical duplicate of an existing notecard (i.e. undid your edits and resaved -- the old asset wasn't deleted, just unlinked from the notecard inventory entry) it gets the original UUID back, and that was still cached.

This was news to me too until right now, but you can confirm it by right click + copy asset UUID -- notecards with the same data in them have the same UUID, even if they weren't direct copies.

Edited by Frionil Fang
  • Like 2
  • Thanks 1
Link to comment
Share on other sites

reading the feedback from those doing testing then unless there is further changes from Linden in how the cache works,  for a one-time sequential read/write into LinksetData then I be going with this code. Which is a more tidy version of what I posted earlier

when the cache is available then use it, when it isn't then continue getting data with standard llGetNotecardLine and dataserver event

// global
string notecardName;
integer notecardLine;
key notecardQuery;

state default
{
    state_entry()
    {
        notecardQuery = llGetNotecardLine(notecardName, notecardLine);
    }

    dataserver(key query, string data)
    {
        if (query == notecardQuery)
        {  
            while (data != EOF)
            {
                ... write data to LinksetData ...

                data = llGetNotecardLineSync(notecardName, ++notecardLine);
                if (data == NAK)
                {
                    notecardQuery = llGetNotecardLine(notecardName, notecardLine);
                    return;           
                }
            } 
        }
    }
}

 

 

Edited by elleevelyn
/
  • Like 2
Link to comment
Share on other sites

More testing. 

the code

    touch_start(integer total_number)
    {
        lineNo = 0;
        llOwnerSay((string)llGetUnixTime() + ": starting read of 1000 lines");
        integer DO_LOOP = TRUE;
        while(DO_LOOP) {
            string result = llGetNotecardLineSync(ncName, lineNo);
            if (result == NAK) {
                llOwnerSay("notecard not ready at line: " + (string)lineNo );
                llGetNotecardLine(ncName, lineNo);
                llSleep(1.0);
            } else if (result == EOF) {
            llOwnerSay((string)llGetUnixTime() + ": all NC lines Read");
            DO_LOOP = FALSE;
            } else {
                ++lineNo;
            }
        }
    }

The test card:

  • Notecard consisted of 1000 lines.  Each line reads: "Notecard test line 0000", and the number incremented to 999.

The output:

  • [18:05] Notecard Sync tester: 1706493914: starting read of 1000 lines
  • [18:05] Notecard Sync tester: notecard not ready at line: 0
  • [18:05] Notecard Sync tester: 1706493916: all NC lines Read

Second output with card firmly in the cache:

  • [18:09] Notecard Sync tester: 1706494162: starting read of 1000 lines
  • [18:09] Notecard Sync tester: 1706494163: all NC lines Read

All this was with no parsing of the data. So,  I did a final test so simulate processing the lines.  So I took the result and edited the final else to do some work of storing the result into the LSD store.

                llStringTrim(result, STRING_TRIM);
                string lsDP = llGetSubString(result, -3, -1);
                llLinksetDataWrite(lsDP, result);
                ++lineNo;
  • [18:16] Notecard Sync tester: 1706494612: starting read of 1000 lines
  • [18:16] Notecard Sync tester: 1706494614: all NC lines Read
  • [18:16] Notecard Sync tester: linkset records stored: 1000

The record count came from "llLinksetDataFoundCount(".*") to verify that I actually counted the records.  this is going to be nice.

Edited by Anna Salyx
  • Like 1
Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...