UR::Context(3pm)

๐Ÿ“ฆ
version 0.470 ยท 2 versions
, in debian bookworm, ubuntu jammy, ubuntu noble

๐ŸŽ€ UR::Context ๐ŸŽ€

Okay bestie, welcome to UR::Context(3pm) from the User Contributed Perl Documentation corner of the universe ๐Ÿ’– This one's a big one so we're doing it in parts, and this is part one. Buckle up, it's giving lore.


โœจ NAME โœจ

UR::Context โ€” manage the current state of the application ๐Ÿซถ


๐ŸŒธ SYNOPSIS ๐ŸŒธ

use AppNamespace;

my $obj = AppNamespace::SomeClass->get(id => 1234);
$obj->some_property('I am changed');

UR::Context->get_current->rollback; # some_property reverts to its original value

$obj->other_property('Now, I am changed');

UR::Context->commit; # other_property now permanently has that value

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

So like, here's the tea: your main application code will rarely poke at UR::Context objects directly โ€” the one exception being the commit and rollback methods, which you will be calling, no cap.

What's it actually doing all day? It manages the mappings between your application's classes, the object cache, and your external data sources. It's the main character running the whole backstage operation, periodt. ๐Ÿ‘‘


๐Ÿ‘‘ SUBCLASSES ๐Ÿ‘‘

UR::Context is an abstract class. When an application starts up, the system creates a handful of Contexts that logically exist within one another, like little nesting dolls ๐Ÿ’…

  1. UR::Context::Root โ€” a context that represents all the data reachable in the application's namespace. It connects the application to external data sources.
  2. UR::Context::Process โ€” a context that represents the state of data within the currently running application. It handles the transfer of data to and from the Root context, through the object cache, on behalf of the application code.
  3. UR::Context::Transaction โ€” a context that represents an in-memory transaction as a diff of the object cache. The Transaction keeps a list of changes to objects and is able to revert those changes with rollback(), or apply them to the underlying context with commit().

๐Ÿงš CONSTRUCTOR ๐Ÿงš

begin

my $trans = UR::Context::Transaction->begin();

UR::Context::Transaction instances are created through begin(). That's the whole vibe check on that one.

A UR::Context::Root and UR::Context::Process context will be created for you when the application initializes. Additional instances of these classes are not usually instantiated โ€” so like, don't go making your own, bestie, the system already handled it ๐Ÿซถ


๐Ÿ’œ METHODS ๐Ÿ’œ

Most of the methods below can be called as either a class or object method of UR::Context. If you call them as a class method, they'll operate on the current context. Easy.


get_current

my $context = UR::Context::get_current();

Returns the UR::Context instance of whatever is the most currently created Context. Can be called as a class or object method โ€” both slay. โœจ


query_underlying_context

my $should_load = $context->query_underlying_context();
$context->query_underlying_context(1);

A property of the Context that sets the default value of the $should_load flag inside get_objects_for_class_and_rule, as described below. Here's the breakdown:

  • Initially its value is undef, meaning that during a get(), the Context will query the underlying data sources only if this query has not been done before.
  • Setting this property to 0 will make the Context never query data sources, meaning that the only objects retrievable are those already in memory.
  • Setting the property to 1 means that every query will hit the data sources, even if the query has been done before.

get_objects_for_class_and_rule

@objs = $context->get_objects_for_class_and_rule(
                      $class_name,
                      $boolexpr,
                      $should_load,
                      $should_return_iterator
                  );

This is the method that serves as the main entry point to the Context behind the get() and is_loaded() methods of UR::Object, and the reload() method of UR::Context. It's the icon doing the actual work ๐Ÿ”ฅ

$class_name and $boolexpr are required arguments, and they specify the target class by name and the rule used to filter the objects the caller is interested in.

$should_load is a flag indicating whether the Context should load objects satisfying the rule from external data sources. Let's get specific:

  • A true value means it should always ask the relevant data sources, even if the Context believes the requested data is in the object cache.
  • A false but defined value means the Context should not ask the data sources for new data, but only return what is currently in the cache matching the rule.
  • The value undef means the Context should use the value of its query_underlying_context property. If that is also undef, then it will use its own judgement about asking the data sources for new data, and will merge cached and external data as necessary to fulfill the request.

$should_return_iterator is a flag indicating whether this method should return the objects directly as a list, or an iterator function instead. If true, it returns a subref that returns one object each time it is called, and undef after the last matching object:

my $iter = $context->get_objects_for_class_and_rule(
                         'MyClass',
                         $rule,
                         undef,
                         1
                     );
my @objs;
while (my $obj = $iter->());
    push @objs, $obj;
}

has_changes

my $bool = $context->has_changes();

Returns true if any objects in the given Context's object cache (or the current Context, if you called it as a class method) have any changes that haven't been saved to the underlying context. Basically: "bestie, are we holding unsaved stuff?" ๐Ÿ™ˆ


commit

UR::Context->commit();

Causes all objects with changes to save their changes back to the underlying context. The way this breaks down:

  • If the current context is a UR::Context::Transaction, then the changes will be applied to whatever Context the transaction is a part of.
  • If the current context is a UR::Context::Process context, then commit() pushes the changes to the underlying UR::Context::Root context, meaning that those changes will be applied to the relevant data sources.

In the usual case, where no transactions are in play and all data sources are RDBMS databases, calling commit() will cause the program to begin issuing SQL against the databases to update changed objects, insert rows for newly created objects, and delete rows from deleted objects as part of an SQL transaction. If all the changes apply cleanly, it will do an SQL commit, or rollback if not.

commit() returns true if all the changes have been safely transferred to the underlying context, and false if there were problems.


rollback

UR::Context->rollback();

Causes all objects' changes for the current transaction to be reversed. It's giving "undo button, but make it serious" ๐Ÿ˜ญ

  • If the current context is a UR::Context::Transaction, then the transactional properties of those objects will be reverted to the values they had when the transaction started.
  • Outside of a transaction, object properties will be reverted to their values when they were loaded from the underlying data source.

rollback() will also ask all the underlying databases to rollback.


clear_cache

UR::Context->clear_cache();

Asks the current context to remove all non-infrastructional data from its object cache.

Heads up bestie: this method will fail and return false if any object has changes. So clean up your changes first ๐Ÿ’€

resolve_data_source_for_object

my $ds = $obj->resolve_data_source_for_object();

For the given $obj object, return the UR::DataSource instance that object was loaded from or would be saved to. If objects of that class do not have a data source, then it will return undef.


resolve_data_sources_for_class_meta_and_rule

my @ds = $context->resolve_data_sources_for_class_meta_and_rule($class_obj, $boolexpr);

For the given class metaobject and boolean expression (rule), return the list of data sources that will need to be queried in order to return the objects matching the rule. In most cases, only one data source will be returned. Simple as that โœจ


infer_property_value_from_rule

my $value = $context->infer_property_value_from_rule($property_name, $boolexpr);

For the given boolean expression (rule), and a property name not mentioned in the rule, but which is a property of the class the rule is against, return the value that property must logically have. It's detective work, honestly ๐Ÿ”

For example, if this object is the only TestClass object where foo is the value 'bar', it can infer that the TestClass property baz must have the value 'blah' in the current context.

my $obj = TestClass->create(id => 1, foo => 'bar', baz=> 'blah');
my $rule = UR::BoolExpr->resolve('TestClass', foo => 'bar);
my $val = $context->infer_property_value_from_rule('baz', $rule);
# val now is 'blah'

object_cache_size_highwater

UR::Context->object_cache_size_highwater(5000);
my $highwater = UR::Context->object_cache_size_highwater();

Set or get the value for the Context's object cache pruning high water mark. The object cache pruner will be run during the next get() if the cache contains more than this number of prunable objects. See the "Object Cache Pruner" section below for more information. ๐Ÿงน


object_cache_size_lowwater

UR::Context->object_cache_size_lowwater(5000);
my $lowwater = UR::Context->object_cache_size_lowwater();

Set or get the value for the Context's object cache pruning high water mark. The object cache pruner will stop when the number of prunable objects falls below this number.


prune_object_cache

UR::Context->prune_object_cache();

Manually run the object cache pruner. Sometimes you just wanna take matters into your own hands ๐Ÿ’…


reload

UR::Context->reload($object);
UR::Context->reload('Some::Class', 'property_name', value);

Ask the context to load an object's data from an underlying Context, even if the object is already cached. With a single parameter, it will use that object's ID parameters as the basis for querying the data source. reload will also accept a class name and list of key/value parameters the same as get. Refresh that data, bestie ๐Ÿฆ‹


_light_cache

UR::Context->_light_cache(1);

Turn on or off the light caching flag. Light caching alters the behavior of the object cache in that all object references in the cache are made weak by Scalar::Util::weaken(). This means that the application code must keep hold of any object references it wants to keep alive. Light caching defaults to being off, and must be explicitly turned on with this method.


and that's part one of three, bestie ๐Ÿ’– part two is right there waiting for you โœจ

๐Ÿ’– Custom observer aspects ๐Ÿ’–

okay so UR::Context is not just gonna sit there quietly โ€” it sends out signals for observers who are watching for some non-standard aspects. like, extra little vibes it broadcasts. here's the whole roster:

precommit

This one fires after commit() has been called, but before any changes get saved to the data sources. ๐Ÿซถ The only parameters the Observer's callback gets are the Context object and the aspect ("precommit"). That's it. Two things. Slay.

commit

Fires after commit() has been called, and after an attempt has been made to save the changes to the data sources. The callback parameters are the Context object, the aspect ("commit"), and a boolean value telling you whether the commit succeeded or not. ๐Ÿ’… so you literally always know if it ate or not.

prerollback

Fires after rollback() has been called, but before and object state is reverted. ๐Ÿ‘€

rollback

Fires after rollback() has been called, and after an attempt has been made to revert the state of all the loaded objects. The callback parameters are the Context object, the aspect ("rollback"), and a boolean value indicating whether the rollback succeeded or not. ๐Ÿ”ฅ


๐ŸŒˆ Data Concurrency ๐ŸŒˆ

okay bestie, this is the part where I hold your hand a lil bit. Currently, the Context is optimistic about data concurrency, meaning that it does very little to prevent clobbering data in underlying Contexts during a commit() if other processes have changed an object's data after the Context has cached and object. ๐Ÿ˜ญ

For example: a database has an object with ID 1 and a property with value 'bob'. A program loads this object and changes the property to 'fred', but does not yet commit(). Meanwhile, another program loads the same object, changes the value to 'joe' and does commit(). Finally the first program calls commit(). The final value in the database will be 'fred', and no exceptions will be raised. ๐Ÿ’€ yeah. it just does that.

As part of the caching behavior, the Context keeps a record of what the object's state is as it's loaded from the underlying Context. This is how the Context knows what object have been changed during commit(). ๐Ÿงš

If an already cached object's data is reloaded as part of some other query, data consistency of each property will be checked. If there are no conflicting changes, then any differences between the object's initial state and the current state in the underlying Context will be applied to the object's notion of what it thinks its initial state is. โœจ

In some future release, UR may support additional data concurrency methods such as pessimistic concurrency: check that the current state of all changed (or even all cached) objects in the underlying Context matches the initial state before committing changes downstream. Or allowing the object cache to operate in write-through mode for some or all classes. ๐Ÿ”ฎ no cap, that's the roadmap vibe.


๐ŸŽ€ Internal Methods ๐ŸŽ€

There are many methods in UR::Context meant to be used internally, but they're worth documenting for anyone interested in the inner workings of the Context code. iykyk. ๐Ÿซถ

_create_import_iterator_for_underlying_context

$subref = $context->_create_import_iterator_for_underlying_context(
                        $boolexpr, $data_source, $serial_number
                    );
$next_obj = $subref->();

This method is part of the object loading process, and is called by get_objects_for_class_and_rule when it is determined that the requested data does not exist in the object cache, and data should be brought in from another, underlying Context. Usually this means the data will be loaded from an external data source. ๐Ÿ‘‘

  • $boolexpr is the UR::BoolExpr rule, usually from the application code.
  • $data_source is the UR::DataSource that will be used to load data from.
  • $serial_number is used by the object cache pruner. Each object loaded through this iterator will have $serial_number in its "__get_serial" hashref key.

It works by first getting an iterator for the data source (the $db_iterator). It calls _resolve_query_plan_for_ds_and_bxt to find out how data is to be loaded and whether this request spans multiple data sources. It calls __create_object_fabricator_for_loading_template to get a list of closures to transform the primary data source's data into UR objects, and _create_secondary_loading_closures (if necessary) to get more closures that can load and join data from the primary to the secondary data source(s). ๐ŸŒธ

It returns a subref that works as an iterator, loading and returning objects one at a time from the underlying context into the current context. It returns undef when there are no more objects to return.

The returned iterator works by first asking the $db_iterator for the next row of data as a listref. Asks the secondary data source joiners whether there is any matching data. Calls the object fabricator closures to convert the data source data into UR objects. If any of the object requires subclassing, then additional importing iterators are created to handle that. Finally, the objects matching the rule are returned to the caller one at a time. ๐Ÿ’…

_resolve_query_plan_for_ds_and_bxt

my $query_plan = $context->_resolve_query_plan_for_ds_and_bxt(
                                  $data_source,
                                  $boolexpr_tmpl
                              );
my($query_plan, @addl_info) = $context->_resolve_query_plan_for_ds_and_bxt(
                                                $data_source,
                                                $boolexpr_tmpl
                                            );

When a request is made that will hit one or more data sources, _resolve_query_plan_for_ds_and_bxt is used to call a method of the same name on the data source. It returns a hashref used by many other parts of the object loading system, and describes what data source to use, how to query that data source to get the objects, how to use the raw data returned by the data source to construct objects and how to resolve any delegated properties that are a part of the rule. โœจ

  • $data_source is a UR::DataSource object ID.
  • $coolexpr_tmpl is a UR::BoolExpr::Template object.

In the common case, the query will only use one data source, and this method returns that data directly. But if the primary data source sets the "joins_across_data_sources" key on the data structure as may be the case when a rule involves a delegated property to a class that uses a different data source, then this methods returns an additional list of data. ๐ŸŒˆ For each additional data source needed to resolve the query, this list will have three items:

  1. The secondary data source ID
  2. A listref of delegated UR::Object::Property objects joining the primary data source to this secondary data source.
  3. A UR::BoolExpr::Template rule template applicable against the secondary data source

_create_secondary_rule_from_primary

my $new_rule = $context->_create_secondary_rule_from_primary(
                             $primary_rule,
                             $delegated_properties,
                             $secondary_rule_tmpl
                         );

When resolving a request that requires multiple data sources, this method is used to construct a rule against applicable against the secondary data source. ๐Ÿฆ‹

  • $primary_rule is the UR::BoolExpr rule used in the original query.
  • $delegated_properties is a listref of UR::Object::Property objects as returned by _resolve_query_plan_for_ds_and_bxt() linking the primary to the secondary data source.
  • $secondary_rule_tmpl is the rule template, also as returned by _resolve_query_plan_for_ds_and_bxt().

_create_secondary_loading_closures

my($obj_importers, $joiners) = $context->_create_secondary_loading_closures(
                                             $primary_rule_tmpl,
                                             @addl_info);

When resolving a request that spans multiple data sources, this method is used to construct two lists of subrefs to aid in the request. ๐ŸŒŸ

  • $primary_rule_tmpl is the UR::BoolExpr::Template rule template made from the original rule.
  • @addl_info is the same list returned by _resolve_query_plan_for_ds_and_bxt. For each secondary data source, there will be one item in the two listrefs that are returned, and in the same order.
  • $obj_importers is a listref of subrefs used as object importers. They transform the raw data returned by the data sources into UR objects. ๐Ÿ’–
  • $joiners is also a listref of subrefs. These closures know how the properties link the primary data source data to the secondary data source. They take the raw data from the primary data source, load the next row of data from the secondary data source, and returns the secondary data that successfully joins to the primary data. You can think of these closures as performing the same work as an SQL "join" between data in different data sources. ๐Ÿ”ฅ

_cache_is_complete_for_class_and_normalized_rule

($is_cache_complete, $objects_listref) =
    $context->_cache_is_complete_for_class_and_normalized_rule(
                  $class_name, $boolexpr
              );

This method is part of the object loading process, and is called by get_objects_for_class_and_rule to determine if the objects requested by the UR::BoolExpr $boolexpr will be found entirely in the object cache. If the answer is yes then $is_cache_complete will be true. $objects_listef may or may not contain objects matching the rule from the cache. If that list is not returned, then get_objects_for_class_and_rule does additional work to locate the matching objects itself via _get_objects_for_class_and_rule_from_cache. ๐Ÿ‘‘

It does its magic by looking at the $boolexpr and loosely matching it against the query cache $UR::Context::all_params_loaded

_get_objects_for_class_and_rule_from_cache

@objects = $context->_get_objects_for_class_and_rule_from_cache(
                         $class_name, $boolexpr
                     );

This method is called by get_objects_for_class_and_rule when _cache_is_complete_for_class_and_normalized_rule says the requested objects do exist in the cache, but did not return those items directly. ๐ŸŽ€

The UR::BoolExpr $boolexpr contains hints about how the matching data is likely to be found. Its "_context_query_strategy" key will contain one of three values

  1. all โ€” this rule is against a class with no filters at all, which basically means "gimme every single member of this class." It calls $class->all_objects_loaded to scoop up all the objects of that class out of the object cache. ๐Ÿ’…
  2. id โ€” this rule is against a class and filters by just one single ID, or a whole list of IDs. The request gets fulfilled by plucking the matching objects straight out of the object cache. No database trip, we love that for us.
  3. index โ€” this rule is against one or more non-id properties. An index gets built mapping the filtered properties and their values over to the cached objects that have those values. Then the request is fulfilled by using that index to find the objects matching the filter. โœจ
  4. set intersection โ€” this one is a group-by rule and it returns a ::Set object.

๐Ÿซถ _loading_was_done_before_with_a_superset_of_this_params_hashref

$bool = $context->_loading_was_done_before_with_a_superset_of_this_params_hashref(
              $class_name,
              $params_hashref
          );

This one is used by _cache_is_complete_for_class_and_normalized_rule to figure out whether the data you're asking for was already asked for before โ€” either from a get() that wanted a superset of what you want now, or from a request on a parent class of the thing you want now. ๐Ÿ‘‘

Say, for example, a get() happens on a class with one param:

@objs = ParentClass->get(param_1 => 'foo');

And then later, another request comes in with an extra param:

@objs2 = ParentClass->get(param_1 => 'foo', param_2 => 'bar');

Then that first request must have returned all the data that could possibly have satisfied the second request, so the system will not issue a query against the data source. Slay, zero wasted work. ๐ŸŽ€

And as another example โ€” given those same two queries that already happened, if another get() goes out on a class that inherits from ParentClass:

@objs3 = ChildClass->get(param_1 => 'foo');

Again, that first request has already loaded all the relevant data, so it won't query the data source. periodt.

๐Ÿ’พ _sync_databases

$bool = $context->_sync_databases();

Kicks off the whole process of committing the Context's changes out to the external data sources. _sync_databases() is the workhorse behind commit.

First it finds all the objects with changes. Then it checks those changed objects for validity with $obj->invalid. If any objects come back invalid, then _sync_databases() fails โ€” no cap, that's a hard stop. Finally it bins all the changed objects by data source, and asks each data source to save those objects' changes. It returns true if every data source was able to save its changes, false otherwise.

๐Ÿช„ _reverse_all_changes

$bool = $context->_reverse_all_changes();

_reverse_all_changes() is the workhorse behind rollback. The undo button of the operation. ๐Ÿ™ˆ

For each class, it goes through each object of that class. If the object is a UR::Object::Ghost โ€” meaning it represents a deleted object โ€” it converts the ghost back to the live version of the object. For other classes, it makes a list of properties that have changed since they were loaded (that's the db_committed hash key in the object, iykyk), and reverts those changes by using each property's accessor method. Rewind. โช


๐ŸŒˆ The Object Cache ๐ŸŒˆ

The object cache is integral to the way the Context works, and it's also the main difference between UR and other ORMs. Main character energy, honestly. Other systems do no caching at all and require the calling application to hold references to any objects it's interested in. So say one part of the app loads data from the database and then gives up its references โ€” then if another part of the app does the same or a similar query, it has to go ask the database all over again. ๐Ÿ˜ญ

UR handles caching of classes, objects and queries so it can avoid asking the data sources for data it already loaded previously. The object cache is essentially a software transaction sitting above whatever database transaction is active. After objects are loaded, any changes, creations or deletions exist only in the object cache, and they are not saved to the underlying data sources until the application explicitly requests a commit or rollback.

Objects are handed back to the application only after they're inserted into the object cache. That means if disconnected parts of the application get back objects with the same class and ID, they'll have references to the exact same object reference, and changes made in one part will be visible to all the other parts of the app. Iconic. An unchanged object can be removed from the object cache by calling its unload() method.

Since changes to the underlying data sources are effectively delayed, it's possible that the application's notion of the object's current state does not match the data stored in the data source. You can mitigate this by using the load() class or object method to fetch the latest data if that's a problem for you.

Another thing to be aware of: if multiple programs are likely to commit conflicting changes to the same data, then whichever one applies its changes last will win โ€” so you need some kind of external locking. ๐Ÿ’€ And finally, if two programs attempt to insert data with the same ID columns into an RDBMS table, the second application's commit will fail, since that will likely violate a constraint. F in the chat.

๐Ÿงš Object Change Tracking

As objects are loaded from their data sources, their properties get initialized with the data from the query, and a copy of that same data is stored in the object in its db_committed hash key. Anyone can ask the object for a list of its changes by calling $obj->changed. Internally, changed() goes through all the object's properties, comparing the current values in the object's hash with the same keys under db_committed.

Objects created through the create() class method have no db_committed, and so the object knows it's a newly created object in this context. Fresh out the box, brand new. โœจ

Every time an object is retrieved with get() or through an iterator, it gets assigned a serial number in its __get_serial hash key from the $UR::Context::GET_SERIAL counter. That number is unique and increases with each get(), and it's used by the "Object Cache Pruner" to expire the least recently requested data.

Objects also track what parameters have been used to get() them, in the hash $obj->{__load}. This is a copy of the data in $UR::Context::all_params_loaded->{$template_id}. For each rule ID, it'll have a count of the number of times that rule was used in a get().

๐Ÿ‘ป Deleted Objects and Ghosts

Calling delete() on an object gets tracked in a different way. First, a new object is created, called a ghost. Ghost classes exist for every class in the application and they're subclasses of UR::Object::Ghost. For example, the ghost class for MyClass is MyClass::Ghost. This ghost object is initialized with the data from the original object. Then the original object is removed from the object cache, and it's reblessed into the UR::DeletedRef class. Any attempt to interact with the object any further will raise an exception. It's giving haunted. ๐Ÿซฃ

Ghost objects are not included in a get() request on the regular class, though the app can ask for them specifically using MyClass::Ghost->get(%params).

Ghost classes do not have ghost classes themselves. Calling create() or delete() on a Ghost class or object will raise an exception. Calling other methods on the Ghost object that exist on the original, live class will delegate over to the live class's method.

๐Ÿ“ all_objects_are_loaded

$UR::Context::all_objects_are_loaded is a hashref keyed by class names. If the value is true, then _cache_is_complete_for_class_and_normalized_rule knows that all the instances of that class exist in the object cache, and it can avoid asking the underlying context/datasource for that class' data.

๐ŸŒธ all_params_loaded

$UR::Context::all_params_loaded is a two-level hashref. The first level is template (UR::BoolExpr::Template) IDs. The second level is rule (UR::BoolExpr) IDs. The values are how many times that class and rule have been involved in a get(). This data is used by _loading_was_done_before_with_a_superset_of_this_params_hashref to determine if the requested data will be found in the object cache for non-id queries.

๐Ÿ’œ all_objects_loaded

$UR::Context::all_objects_loaded is a two-level hashref. The first level is class names. The second level is object IDs. Every time an object is created, defined or loaded from an underlying context, it gets inserted into the all_objects_loaded hash. For queries involving only ID properties, the Context can retrieve them directly out of the cache if they appear there.

The entire cache can be purged of non-infrastructional objects by calling clear_cache.

๐ŸŽ€ Object Cache Pruner

The default Context behavior is to cache all objects it knows about for the entire life of the process. For programs that churn through large amounts of data, or that live for a long time, this is probably not what you want. Bestie, your RAM is crying. ๐Ÿ˜ญ

The Context has two settings to loosely control the size of the object cache: object_cache_size_highwater and object_cache_size_lowwater. As objects are created and loaded, a count of uncachable objects is kept in $UR::Context::all_objects_cache_size. The first part of get_objects_for_class_and_rule checks to see if the current size is greater than the highwater setting, and calls prune_object_cache if so.

prune_object_cache() works by looking at what $UR::Context::GET_SERIAL was the last time it ran, and what it is now, and making a guess about what object serial number to use as a guide for removing objects โ€” starting at 10% of the difference between the last serial and the current value, called the target serial.

Then it starts executing a loop as long as $UR::Context::all_objects_cache_size is greater than the lowwater setting. For each uncachable object, if its __get_serial is less than the target serial, it gets weakened from any UR::Object::Indexes it may be a member of, and then weakened from the main object cache, $UR::Context::all_objects_loaded.

The application may lock an object in the cache by calling __strengthen__ on it. Likewise, the app may hint to the pruner to throw away an object as soon as possible by calling __weaken__. โœจ


๐Ÿ‘‘ SEE ALSO

  • UR::Context::Root
  • UR::Context::Process
  • UR::Object
  • UR::DataSource
  • UR::Object::Ghost
  • UR::Observer

and that's a wrap on the whole page, bestie!! ๐Ÿฆ‹ this one was shipped with perl v5.32.1 and dated 2022-01-17 โ€” go forth and cache responsibly, mwah ๐Ÿ’…๐Ÿ’–

๐Ÿ“ฆ where this page comes from