Jump to content

llGetNotecardLineSync()


Cain Maven
 Share

You are about to reply to a thread that has been inactive for 81 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

The new and wonderful llGetNotecardLineSync() function requires that the notecard is in the region's asset cache. Is there a way of ensuring this?

For example, will calling llGetNumberOfNotecardLines() immediately before reading lines as in the published example do this? Also, is it possible that the notecard is removed from the cache while it is being read? 

Depending on how this all works, using this function may require a lot of checks and hand holding. The speed improvements are of course very welcome :)

 

Link to comment
Share on other sites

1 hour ago, Cain Maven said:

 llGetNotecardLineSync()  ... using this function may require a lot of checks and hand holding

yes. Will need to pay attention to NAK . On NAK then retry that line. If retries continue to return NAK then at some point willl have to drop down to standard llGetNotecardLine and dataserver event

 

  • Thanks 1
Link to comment
Share on other sites

We'll have to see how it goes in practice, but I suspect the common pattern will be;

Start off each time with either llGetNotecardLineSync, or llGetNumberOfNotecardLines, and then in the dataserver event, go nuts reading it with llGetNotecardLineSync.

Your old-style read can likely just be a dummy read, you don't actually need to use the returned data, just use the dataserver event as telling you the notecard is safely loaded into the sim's cache.  There's a bunch of potential edge cases there, and I haven't seen any hint of whether or how LL are dealing with them, but an awful lot of scripts about today will just lock up and die should a single dataserver event fail to arrive (the most common example codes I see everywhere have that issue), and they mostly continue to work, so, I expect this to at least be better than that (a little extra hand-holding might actually be an improvement).

Edited by Bleuhazenfurfle
Link to comment
Share on other sites

Three patterns come to mind:

  1. Drop-in replacement for much slower existing notecard-reading, just triggering that preliminary dataserver event to get the notecard cached and then loop through reading all lines into memory
  2. Read and process each line on demand in sequence, as the script is ready for another line, not storing all the lines in memory (and here synchronously and not necessarily in the dataserver handler.
  3. Use notecards as ISAM files, reading the index from the top of the notecard, then fetch random lines as needed

Other than the structural advantage of synchronous calling, these were all possible with async reads, too, but with speed and synchronous structure #3 will be more often worthwhile (and be preferable to LSD in some cases).

  • Thanks 1
Link to comment
Share on other sites

On 1/25/2024 at 1:47 PM, Bleuhazenfurfle said:

Start off each time with either llGetNotecardLineSync, or llGetNumberOfNotecardLines, and then in the dataserver event, go nuts reading it with llGetNotecardLineSync.

Sigh…  Just noticed a rather important typo…  that first llGetNotecardLineSync was supposed to be a regular llGetNotecardLine.

The basic patterns of how a notecard is used, what they contain, where their contents get stored, etc., don't change in the slightest.  It's still just a function that reads lines of a notecard.  Neither the notecards, nor what you do with them, has changed.  Should also be noted however, that much of the simplicity many seem to be expecting is negated by the expectation that the notecard can get evicted from the sims cache at some point.  That's less likely to happen in a tight processing loop just screaming through the notecard, but random reads over time (like a "joke of the day" script that says a random line every so often) will almost certainly occasionally fail and have to resort to the usual slow method — so for random reads, it may actually be easier to just keep using the regular slow version since that way you don't need to handle two separate execution paths (one immediate, the other via a dataserver event).  Just like LSD, this isn't a solution to every conceivable notecard issue ever, and in fact, this adds a small amount of additional complexity which you'll need to deal with to make use of the extra speed it offers (unless you're happy with it failing from time to time).

What has changed, is that once we know the notecard is cached on the sim, we can do a fast loop that processes the notecard very quickly, rather than waiting 0.1s, plus an event invocation, between each and every line.  And we can probably (but we don't actually know) assume that the notecard will remain cached during that loop — however, loops pause for frame or so periodically, and so far as any of us know, there is therefore the possibility the notecard could get evicted from the cache during such a loop.  (LL may be able to offer some insight on that possibility, or, more likely, we'll just have to wait and see.)  Which was what I was trying to get at in my previous post — we likely still have to write our scripts assuming the notecard will not happen to be in memory when we want it, and perhaps even assuming those fast loops won't get all the way through before they have to fall back to another dataserver event.  So I expect to mostly see llGetNotecardLineSync within the regular dataserver events, not replacing them.

Edited by Bleuhazenfurfle
Link to comment
Share on other sites

It seems like there's pretty much a consensus here: Yes, the speed gain is great, but it does need a lot of hand holding.

It makes you wonder if this could have been approached differently -- although without knowing the internals of the caching system it's fairly pointless to speculate.

Besides, there are a few other things that perhaps should have higher priority for the Lab :)

  • Like 1
Link to comment
Share on other sites

other than the example pcode script nothing in here about the server is true if what is in a subsequent post is true

 

reading thru what everyone has said and thinking more about how NAK occurs then my guess is the server design is something like this on every llGetNotecardLineSynch call

if (notecard in cache)
    return line
else
{
    fetch notecard from datastore and cache
    if (fetch successful in X time)
        return line
    else
        return NAK
}  

if this is true then NAK is the result of region server not being able to re/fetch and cache the notecard in a timely manner

if so then a way to cater for NAK recovery while doing a sequential read can go something like

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

// local call to get started
notecardQuery = llGetNotecardLine(notecardName, notecardLine);


dataserver(key query, string data)
{
    if (query == notecardQuery)
    {   // data should never be NAK at this moment ??
       if (data != EOF)
       {
           ... do something with data ...

           integer continue = TRUE;
           do
           {
                data = llGetNotecardLineSynch(notecardName, ++notecardLine);
                if (data == NAK)
                {  // is cache issue so revert to llGetNotecardLine for this line
                    continue = FALSE;
                    notecardQuery = llGetNotecardLine(notecardName, notecardLine);               
                }
                else if (data != EOF)
                {
                    ... do something with data ...

                }
           } while (data != EOF && continue);
       }
    }
}

 

 

Edited by elleevelyn
dataserver
Link to comment
Share on other sites

My guess, is it's much more like:

if (notecard in cache)
    return line
else
{
  /* -- maybe:
    fetch notecard from datastore (and cache)
  */
  /* -- but not:
    if (fetch successful in X time)
        return line
    else
  */
        return NAK
}  

One key thing to realise is there is exactly one function to pause a script — llSleep — every other LSL built in function is essentially synchronous + delay (which so far as I can tell, is basically also llSleep).  Pretty much all the "asynchronous" commands are actually a small "fire-and-forget" synchronous one, with the asynchonicity being little more than an intentional side effect, and an echoed parameter — so it's exceedingly unlikely there'll be any "in X time" in there.

Link to comment
Share on other sites

1 hour ago, Bleuhazenfurfle said:

My guess, is it's much more like:

if (notecard in cache)
    return line
else
{
  /* -- maybe:
    fetch notecard from datastore (and cache)
  */
  /* -- but not:
    if (fetch successful in X time)
        return line
    else
  */
        return NAK
}  

Pretty much all the "asynchronous" commands are actually a small "fire-and-forget" synchronous one, with the asynchonicity being little more than an intentional side effect, and an echoed parameter — so it's exceedingly unlikely there'll be any "in X time" in there.

yes this makes sense both for the /* -- maybe and /* -- but not

edit add: altho if it didn't wait for some X time when fetching the notecard to cache then wouldn't it always NAK on the very 1st llGetNotecardLineSync call ? I can't actually test this as i can't go to a Linden Magnum sandbox and I dunno where elsewhere there might be a Magnum region

Edited by elleevelyn
Link to comment
Share on other sites

I wonder whether the notecard cache clears every region restart. Even though it might happen for other reasons too (so we still need to wrap each -Sync call with a NAK test), I might use CHANGED_REGION_START as trigger to re-cache a notecard for quick line reads outside dataserver().

  • Like 1
Link to comment
Share on other sites

50 minutes ago, Love Zhaoying said:

I use the official viewer, which doesn't recognize llGetNotecardLineSync() or the constant NAK yet.

Do I need both a special viewer version and to be on a specific RC channel?

What's the secret sauce for actually testing this?

 

is not on Main Server release, nor on Le Tigre. if is on anything on Main Grid then be on Magnum, otherwise Beta Grid. With a Premium account then should be able to access region: "Magnum Sandbox A" and check

not sure if is even on Magnum tho as Signal Linden said on 13 January 2024 is "In Progress" https://feedback.secondlife.com/scripting-features/p/add-llgetnotecardlinefast

the LSL wiki entry went up on 17 January

also just read the following exchange on there. maybe primer1 will come by here and shed more light

Quote

 

Kadah Coba
 
Dumb question, but how would one cause a notecard to become cached in the first place so that these functions will work?
This description here and on the wiki conflict in this regard: "If the notecard has not been previously cached on the simulator it will return the NAK constant."
Also, please define what "NAK", preferably create the currently non-existing wiki page. 😛
 
·
Reply
 
primerib1 Resident
 
Kadah Coba: Signal told me that you load the notecard into the cache using
llGetNotecardLine() or llGetNumberOfNotecardLines()
 
·
Reply

 

 

so if this holds true then at least we know how the caching is triggered

 

Link to comment
Share on other sites

13 minutes ago, elleevelyn said:

is not on Main Server release, nor on Le Tigre. if is on anything on Main Grid then be on Magnum, otherwise Beta Grid. With a Premium account then should be able to access region: "Magnum Sandbox A" and check

not sure if is even on Magnum tho as Signal Linden said on 13 January 2024 is "In Progress" https://feedback.secondlife.com/scripting-features/p/add-llgetnotecardlinefast

the LSL wiki entry went up on 17 January

also just read the following exchange on there. maybe primer1 will come by here and shed more light

 

so if this holds true then at least we know how the caching is triggered

 

Thanks for the info. 🤦

In my case, "testing" it is not worth my time and effort, if it will be released soon. (I have survived 17 years without ever using the Beta grid, and I won't use it just for testing something like this. I'm Premium Plus, so that's not the issue.) I trust it will work fine as described.  I did see another thread awhile back about it, and forgot about it entirely until this thead.

I've coded my main notecard loader with options to use the new function llGetNotecardLineSync() and constant "NAK" commented out, ready to use whenever it is on the main grid.

The function is documented in the Wiki with no note such as, "This function is not yet available".  (How hard would that have been? LOL!)

The thread also didn't seem to mention it wasn't available yet. 🙂  So I assumed stupidly that it was available! 🤪I suppose perhaps if my first post was phrased as a question, someone would have replied pointing out it is not available.

I am following the basic approach #1 that @Qie Niangaogave - read the first line asynchronously via the dataserver() event, then assume the notecard will be available for synchronous reading after that first read.  The script I am using currently loads the entire notecard into LSD. So, that's a perfect use-case for me if it works.

 

 

Link to comment
Share on other sites

I too am confused about the deployment of this function. Inara Pey's blog rightly notes that at the last Simulator User Group meeting, the function was said to be slated to roll out to RCs on the 24th:

Quote
  • Wednesday, January 24th should see a further attempt to deploy the Falls Colours simulator update (which includes a fix for collision sounds reverberating; llRezObjectWithParams, llListFindListNext and llGetNotecardLineSync.

but the Release Notes for that version on Magnum (and probably all RCs) list the other two functions (and more) but not llGetNotecardLineSync, and I can't get it to compile on a Magnum sandbox nor my home region, a Ferrari RC running the same version.

  • Thanks 2
Link to comment
Share on other sites

11 minutes ago, Love Zhaoying said:

I am following the basic approach #1 that @Qie Niangaogave - read the first line asynchronously via the dataserver() event, then assume the notecard will be available for synchronous reading after that first read.  The script I am using currently loads the entire notecard into LSD. So, that's a perfect use-case for me if it works.

 

 

looks like Qie was pretty spot on with Pattern 1 in how this is going to work for sequential reads

Link to comment
Share on other sites

19 minutes ago, Qie Niangao said:

I too am confused about the deployment of this function. Inara Pey's blog rightly notes that at the last Simulator User Group meeting, the function was said to be slated to roll out to RCs on the 24th:

but the Release Notes for that version on Magnum (and probably all RCs) list the other two functions (and more) but not llGetNotecardLineSync, and I can't get it to compile on a Magnum sandbox nor my home region, a Ferrari RC running the same version.

Sounds like "very soon™️ now"!

 

  • Haha 1
Link to comment
Share on other sites

The reason I wanted to go ahead and try this is: My main note loader (that loads LSD with a large Schema) takes 256sec to load, currently.  Of course it started faster, and if I just combine more entries onto "same notecard lines" it will load faster.

But this will make a huge difference.  Luckily, once I am done with development, you only load the Schema "once" into LSD from the notecard.  

 

Link to comment
Share on other sites

The function is available on the beta grid in the Cloud Sandbox region so I went to snoop a bit.

The constant NAK consists of 3 characters 0x0A, 0x15, 0x0A, which translates to newline, NAK, newline (compare to EOF which is newline, newline, newline).

You do not need to use the dataserver event at all. You just must make the call to cache the notecard, you simply can ignore the result and try the same line again after that. This example script also loads the notecard via the standard method for very loose timing comparison: loading and printing a 110 line* notecard that was not cached took ~0.6 seconds with the sync method, ~14.8 s with the classic.

*NOTE: at the time of this writing the llGetNotecardLineSync AND llGetNotecardLine are broken on this beta server: they never return the last line and return EOF prematurely.

integer sync_read(string name) {
    integer i;
    string line;
    integer retries;
    while((line = llGetNotecardLineSync(name, i)) != EOF) {
        if(line == NAK) {
            llGetNumberOfNotecardLines(name); // load into cache, ignore the dataserver event
            // safeguard against repeat errors: this could happen if you load by UUID
            // that is invalid; it will never give a script error, nor give up returning NAK
            // normally it will be available after the first attempt
            if(++retries > 10)
                return FALSE;
        } else {
            llOwnerSay((string)i + ": " + line);
            ++i;
        }
    }
    llOwnerSay("Used " + (string)retries + " retries");
    return TRUE;
}

// for normal async read
string nc_name;
key nc_handle;
integer nc_line;

default
{
    state_entry() {
        nc_name = "40fb312a-289f-47ac-d987-7fa05a1df787";
        llOwnerSay("--- Sync read ---");
        llResetTime();
        if(sync_read(nc_name)) {
            llOwnerSay("Took " + (string)llGetTime() + " seconds");
        } else {
            llOwnerSay("Notecard could not be read");
        }
        llOwnerSay("--- Async read start ---");
        llResetTime();
        nc_handle = llGetNotecardLine(nc_name, nc_line);
    }

    // only used by async read, the dataserver request by the sync read is ignored
    dataserver(key id, string data) {
        if(id != nc_handle)
            return;
        if(data == EOF) {
            llOwnerSay("Took " + (string)llGetTime() + " seconds");
            return;
        }
        llOwnerSay((string)nc_line + ": " + data);
        nc_handle = llGetNotecardLine(nc_name, ++nc_line);
    }
}

 

  • Thanks 2
Link to comment
Share on other sites

10 hours ago, Frionil Fang said:

You do not need to use the dataserver event at all. You just must make the call to cache the notecard, you simply can ignore the result and try the same line again after that. This example script also loads the notecard via the standard method for very loose timing comparison: loading and printing a 110 line* notecard that was not cached took ~0.6 seconds with the sync method, ~14.8 s with the classic.

integer sync_read(string name) {
    integer i;
    string line;
    integer retries;
    while((line = llGetNotecardLineSync(name, i)) != EOF) {
        if(line == NAK) {
            llGetNumberOfNotecardLines(name); // load into cache, ignore the dataserver event
            // safeguard against repeat errors: this could happen if you load by UUID
            // that is invalid; it will never give a script error, nor give up returning NAK
            // normally it will be available after the first attempt
            if(++retries > 10)
                return FALSE;
        } else {
            llOwnerSay((string)i + ": " + line);
            ++i;
        }
    }
    llOwnerSay("Used " + (string)retries + " retries");
    return TRUE;
}

That's totally horrible…!  (And also totally expected.)  Was talking to someone about exactly just that the other day.

And because we all *know* someone is going to do exactly that…  The typical use case for something a function like that, is to be able to assume that it worked, and have it return the value — which your retry bail doesn't allow.  Changing that `return FALSE` to just `llSleep(retries)`, with a lower minimum retry count (like, 3) would probably be adequate — try forever, but there's clearly something drastically wrong, and we don't want to contribute to the problem, so slow down drastically until it clears.  (Another option would be just `llSleep(++retries/10)`, with no if() at all.)

Would be nice to know the average time to actually load a notecard, though.  And to get some idea of how long notecards will typically remain cached for (LL should know, if they've been caching them as long as I was recently reminded).

Link to comment
Share on other sites

34 minutes ago, Gabriele Graves said:

It would have been better if the new function just went and fetched the darn notecard if it wasn't already cached and always returned after it was available.

Imagine that 😊

Edited by Cain Maven
  • Like 1
Link to comment
Share on other sites

26 minutes ago, Gabriele Graves said:

It would have been better if the new function just went and fetched the darn notecard if it wasn't already cached and always returned after it was available.

I know that's easy and requires less thought, but I actually disagree.  I kinda like knowing when functions will return (even, THAT functions will return!), and I'd rather it didn't just lock up my script for ages (or crash it with an error) if the asset server goes down or something.

This way, we get to chose how long to wait, how to handle a persistent error, etc.  That said, I know an awful lot of people don't…  There's a lot of scripts out there that just lock up and die if the asset server goes down, and for that and other similar reasons, it's not uncommon to just delete a thing and rez a new one whenever it stops working, and put llScriptReset in your on_rez handler.

And the amount of effort LL would have to put in to making llGetNotecardSync behave that way, without catching on any of those edge cases, just isn't worth it, especially when we can simply do something like above, or, a simple "if not sync, then slow".

Just a little tweak to the way we've been doing it already, gives us the best of both worlds.  I think that's what I'll be doing.

Link to comment
Share on other sites

In reality the majority of scripts are going to want to do the slow version if it's not cached anyway.  I think wanting to do complex other stuff instead is going to be a minor use-case.  For that, the majority pays with unnecessary complication.

Edited by Gabriele Graves
  • Like 2
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 81 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...