Difference between revisions of "LlUpdateKeyValue"

From Second Life Wiki
Jump to navigation Jump to search
(Created page with "Category:Experience Tools {{LSL_Function |func=llUpdateKeyValue |func_desc=Start an asynchronous transaction to update a key-value pair associated with the given experience k…")
 
(7 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Category:Experience Tools]]
[[Category:Experience Tools]]
{{LSL_Function
{{LSL_Function
|inject-2=
{{LSL Function/KeyValue|k|v|value=value|d2_type=string|d2_name=value}}
{{LSL Function/Experience|true}}
|func=llUpdateKeyValue
|func=llUpdateKeyValue
|func_desc=Start an asynchronous transaction to update a key-value pair associated with the given experience key with the given key and value.
|func_desc=Start an asynchronous transaction to update a key-value pair associated with the script's {{LSLGC|Experience}} with the given key ({{LSLPT|k}}) and value ({{LSLPT|v}}).
|func_footnote=Optionally, if "checked" is set to TRUE and an original value is given, then the update will check the current value before applying the update. If the supplied original value doesn't match the current value in the key-value store, the update will ''not'' be applied. This allows the caller to be certain that their update was "atomic" and no other script changed the value between when they read it and when they are updating it.
|return_type=key|return_subtype=handle
 
|return_text=that can be used to identify the corresponding [[dataserver]] event to determine if this command succeeded or failed and the results.
The dataserver callback parameters are:
|p1_type=string|p1_name=k
* A key containing the transaction ID returned from llUpdateKeyValue
|p2_type=string|p2_name=v
* A string containing a comma-delimited list.  The first item is a boolean specifying if the transaction succeeded (1) or not (0).  The second item is a string with a status/error message if it failed or the value if the transaction succeeded.
|p3_type=integer|p3_subtype=boolean|p3_name=checked|p3_desc=If [[TRUE]] the update will only happen if {{LSLPT|original_value}} matches the value in the key-value store.
|return_type=key
|p3_hover=If TRUE the update will only happen if "original_value" matches the value in the key-value store.
|return_text=Returns the ID of the transaction
|p4_type=string|p4_name=original_value|p4_desc=The value to compare with the current value in the key-value store.
|p1_type=string|p1_name=k|p1_desc=They key for the key-value pair
|p4_hover=The value to compare with the current value in the key-value store.
|p2_type=string|p2_name=v|p2_desc=The value for the key-value pair.  Maximum 2047 characters, or 4095 characters if using Mono.
|func_footnote=If {{LSLPT|checked}} is set to [[TRUE]] then the update will only happen if {{LSLPT|original_value}} matches the current value in key-value store, otherwise the [[dataserver]] will return a failure along with the error [[XP_ERROR_RETRY_UPDATE]]. This can be used to create an in-use flag so that {{Wikipedia|Atomicity_(database_systems)|Atomicity}} can be achieved.<br/>
|p3_type=integer|p3_name=checked|p3_desc=TRUE if the original value is given and the update should be "checked" to make sure it is "atomic".
<br/>
|p4_type=string|p4_name=ov|p4_desc=The original value to compare with the current value in the key-value store to make sure the update is "atomic".
As of Jan 1, 2016 maximum bytes is 1011 for key and 4095 for value for both LSO and Mono scripts.<br/>
Using llUpdateKeyValue to update a key that does not exist will not generate [[XP_ERROR_KEY_NOT_FOUND]]. Instead, it will generate a new key with the specified value, as if you had used llCreateKeyValue.  
|also_functions=
|also_functions=
*[[llGetExperienceErrorMessage]]
*[[llGetExperienceErrorMessage]]
Line 23: Line 27:
*[[llKeyCountKeyValue]]
*[[llKeyCountKeyValue]]
*[[llKeysKeyValue]]
*[[llKeysKeyValue]]
|examples=<lsl>key trans;
|examples=<source lang="lsl2">key trans;
default
default
{
{
Line 30: Line 34:
         trans = llUpdateKeyValue("FOO", "BLAH", TRUE, "BAR");
         trans = llUpdateKeyValue("FOO", "BLAH", TRUE, "BAR");
     }
     }
}


dataserver(key t, string value)
    dataserver(key t, string value)
{
    if (t == trans)
     {
     {
        // our llUpdateKeyValue transaction is done
         if (t == trans)
        list result = llCSV2List(value);
         if (llList2Integer(result, 0) == 1)
         {
         {
             // the key-value pair was successfully updated
             // our llUpdateKeyValue transaction is done
            llSay(0, "New key-value pair was successfully updated");
            list result = llCSV2List(value);
            if (llList2Integer(result, 0) == 1)
            {
                // the key-value pair was successfully updated
                llSay(0, "New key-value pair was successfully updated");
            }
            else
            {
                integer error = llList2Integer(result, 1);
                if(error == XP_ERROR_RETRY_UPDATE)
                    llSay(0, "Key-value update failed, checked value is out of date");
                else
                    llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage(error) );
            } 
        }
    }
}</source>
 
This script demonstrates how to avoid update conflicts (two scripts updating the store at the same time), performing fully atomic updates is more complicated. If all scripts writing to the key-value store abide by the virtual lock ($DB_Lock), and only do updates in update_db state, then all writes will be atomic.
<source lang="lsl2">key tid;
list tids;
 
default {
    state_entry() {
        state lock_db;
    }
}
 
state lock_db {
    state_entry() {
        tid = llUpdateKeyValue("$DB_Lock", "LOCK", TRUE, "unlock");
    }
    dataserver(key did, string value) {
        if(did == tid) {
            string payload = llDeleteSubString(value, 0, 1);
            if(llGetSubString(value+",", 0, 1) == "1,"){
                llUpdateKeyValue("$DB_LockedBy", llDumpList2String([llGetOwner(),llGetKey(),llGetLinkKey(!!llGetLinkNumber()),llGetRegionName(),llGetPos(),llGetAttached()],":"), FALSE, "");
                state update_db;
            } else {
                integer err = (integer)payload;
                if(err == XP_ERROR_RETRY_UPDATE) {
                    llSay(0, "Database is already locked!");
                } else {
                    llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage(err) );
                }
                state error;
            }
         }
         }
         else if(llList2Integer(result, 1) == "retry update")
    }
         {
}
             llSay(0, "Key-value update failed, checked value is out of date");
 
state update_db {
    state_entry() {
        tids = [
            llUpdateKeyValue("CatsPermissable", "5", FALSE, ""),
            llUpdateKeyValue("MonkeyMutations", "3", FALSE, ""),
            llUpdateKeyValue("CodFlavorSupport", "NEVER", FALSE, "")
        ];
    }
    dataserver(key did, string value) {
        integer i = llListFindList(tid, [did]);
         if(~i) {
            string payload = llDeleteSubString(value, 0, 1);
            if(llGetSubString(value+",", 0, 1) == "1,"){
                tids = llDeleteSubList(tids, i, i);
                if(tids == []) {
                    state unlock_db;
                }
            } else {
                llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage((integer)payload) );
                state error;
            }
        }
    }
}
 
state unlock_db {
    state_entry() {
        tid = llUpdateKeyValue("$DB_Lock", "unlock", TRUE, "LOCK");
    }
    dataserver(key did, string value) {
         if(did == tid) {
            string payload = llDeleteSubString(value, 0, 1);
            if(llGetSubString(value+",", 0, 1) == "1,"){
                state done;
             } else {
                integer err = (integer)payload;
                if(err == XP_ERROR_RETRY_UPDATE) {
                    llSay(0, "Someone has violated the database lock!");
                } else {
                    llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage(err) );
                }
                state error;
            }
         }
         }
        else
    }
        {
}
            llSay(0, "Key-value update failed: " + llList2String(result, 1) );
 
        }
state done {
     }  
    state_entry(){;}
}</lsl>}}
}
 
state error {
     state_entry(){;}
}</source>
|cat1=Experience
|cat2=Experience/Data
|cat3=Dataserver
|caveats=* It is recommended that keys do not contain commas due to [[llKeysKeyValue]] returning keys in CSV format.
}}

Revision as of 15:16, 2 June 2016

Summary

Function: key llUpdateKeyValue( string k, string v, integer checked, string original_value );

Start an asynchronous transaction to update a key-value pair associated with the script's Experience with the given key (k) and value (v).
Returns a handle (a key) that can be used to identify the corresponding dataserver event to determine if this command succeeded or failed and the results.

• string k The key for the key-value pair
• string v The value for the key-value pair. Maximum 2047 characters, or 4095 if using Mono.
• integer checked If TRUE the update will only happen if original_value matches the value in the key-value store.
• string original_value The value to compare with the current value in the key-value store.

If checked is set to TRUE then the update will only happen if original_value matches the current value in key-value store, otherwise the dataserver will return a failure along with the error XP_ERROR_RETRY_UPDATE. This can be used to create an in-use flag so that "Wikipedia logo"Atomicity can be achieved.

As of Jan 1, 2016 maximum bytes is 1011 for key and 4095 for value for both LSO and Mono scripts.
Using llUpdateKeyValue to update a key that does not exist will not generate XP_ERROR_KEY_NOT_FOUND. Instead, it will generate a new key with the specified value, as if you had used llCreateKeyValue.

For this function to work, the script must be compiled into an Experience.

Specification

Dataserver

The dataserver callback parameters are:

String Components
• integer success A boolean specifying if the transaction succeeded (1) or not (0).
• integer error An XP_ERROR_* flag that describes why the operation failed.
• string value The value for the key-value pair. Maximum 2047 characters, or 4095 if using Mono. Note! This value may contain commas.

Caveats

  • If you recompile a script that was previously associated with an Experience but do so with a client that lacks the ability to compile scripts into an experience the script will lose the associated Experience.
  • It is recommended that keys do not contain commas due to llKeysKeyValue returning keys in CSV format.
All Issues ~ Search JIRA for related Bugs

Examples

key trans;
default
{
    state_entry()
    {
        trans = llUpdateKeyValue("FOO", "BLAH", TRUE, "BAR");
    }

    dataserver(key t, string value)
    {
        if (t == trans)
        {
            // our llUpdateKeyValue transaction is done
            list result = llCSV2List(value);
            if (llList2Integer(result, 0) == 1)
            {
                // the key-value pair was successfully updated
                llSay(0, "New key-value pair was successfully updated");
            }
            else
            {
                integer error = llList2Integer(result, 1);
                if(error == XP_ERROR_RETRY_UPDATE)
                    llSay(0, "Key-value update failed, checked value is out of date");
                else
                    llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage(error) );
            }  
        }
    }
}

This script demonstrates how to avoid update conflicts (two scripts updating the store at the same time), performing fully atomic updates is more complicated. If all scripts writing to the key-value store abide by the virtual lock ($DB_Lock), and only do updates in update_db state, then all writes will be atomic.

key tid;
list tids;

default {
    state_entry() {
        state lock_db;
    }
}

state lock_db {
    state_entry() {
        tid = llUpdateKeyValue("$DB_Lock", "LOCK", TRUE, "unlock");
    }
    dataserver(key did, string value) {
        if(did == tid) {
            string payload = llDeleteSubString(value, 0, 1);
            if(llGetSubString(value+",", 0, 1) == "1,"){
                llUpdateKeyValue("$DB_LockedBy", llDumpList2String([llGetOwner(),llGetKey(),llGetLinkKey(!!llGetLinkNumber()),llGetRegionName(),llGetPos(),llGetAttached()],":"), FALSE, "");
                state update_db;
            } else {
                integer err = (integer)payload;
                if(err == XP_ERROR_RETRY_UPDATE) {
                    llSay(0, "Database is already locked!");
                } else {
                    llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage(err) );
                }
                state error;
            }
        }
    }
}

state update_db {
    state_entry() {
        tids = [
            llUpdateKeyValue("CatsPermissable", "5", FALSE, ""),
            llUpdateKeyValue("MonkeyMutations", "3", FALSE, ""),
            llUpdateKeyValue("CodFlavorSupport", "NEVER", FALSE, "")
        ];
    }
    dataserver(key did, string value) {
        integer i = llListFindList(tid, [did]);
        if(~i) {
            string payload = llDeleteSubString(value, 0, 1);
            if(llGetSubString(value+",", 0, 1) == "1,"){
                tids = llDeleteSubList(tids, i, i);
                if(tids == []) {
                    state unlock_db;
                }
            } else {
                llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage((integer)payload) );
                state error;
            }
        }
    }
}

state unlock_db {
    state_entry() {
        tid = llUpdateKeyValue("$DB_Lock", "unlock", TRUE, "LOCK");
    }
    dataserver(key did, string value) {
        if(did == tid) {
            string payload = llDeleteSubString(value, 0, 1);
            if(llGetSubString(value+",", 0, 1) == "1,"){
                state done;
            } else {
                integer err = (integer)payload;
                if(err == XP_ERROR_RETRY_UPDATE) {
                    llSay(0, "Someone has violated the database lock!");
                } else {
                    llSay(0, "Key-value update failed: " + llGetExperienceErrorMessage(err) );
                }
                state error;
            }
        }
    }
}

state done {
    state_entry(){;}
}

state error {
    state_entry(){;}
}

Notes

Compiling

For a script to be associated with an Experience...

  • It must be compiled with a client that is Experience aware,
  • The "Use Experience" checkbox must be checked,
  • And one of the users Experience keys selected.
KBcaution.png Important: Not all TPVs have this functionality.

Deep Notes

Search JIRA for related Issues

Signature

function key llUpdateKeyValue( string k, string v, integer checked, string original_value );