- Lazy Extensions for Faster Startup Time
- Glossary
- Migration
- Extension Startup Behaviour
- Startup Timeline
- Automatic Unsnooze Phase (Conditional)
- User Configurability
- Lazy.xml Reference
- Lazy.xml Use Case Examples
- Complete Example
- Tweaking Lazy Behaviour During Development
- Registering Data Catalogs
- FAQ
Lazy Extensions for Faster Startup Time
We have introduced a lazy-load Extensions startup system for CET, in which the initialization of an extension is delayed until it is needed.
Some key points:
- UltraLazyExtensions does not load or compile any extension code at startup.
- All the information that is needed at startup is stored in an XML configuration file called lazy.xml, which is placed alongside the extension.xml.
- The extension will wake up in the following situations:
- if the toolbox is clicked
- if another dependent extension is activated
- an action is performed in which a trigger is defined
- load a drawing that contains snappers from that extension
- other situations mentioned in the below sections
- Renaming lazy.xml to notlazy.xml will result in the extension no longer being lazy, however, the startup behaviors like toolboxes and quickbar configs will still be built according to the definitions in the notlazy.xml.
Glossary
Lazy: A loading strategy to delay the initialization of an extension until it is needed, using the information in lazy.xml for startup behaviors.
Snooze: The state in which the Extension is turned on but hasn't been initialized by the lazy system.
Unsnooze / Wake: The act of activating the Extension to actually load the library or compile the code of the extension.
Migration
Don't want to pay developers to watch CET Designer restart over and over every day when each restart takes minutes? And make sure that your extensions works well, even for users with lots of extensions installed? By making your extensions lazy, startup time can be greatly reduced.
By adding a lazy.xml file to your extension directory (where extension.xml lives), your extension will be lazy. The lazy.xml describes the minimal startup information needed such as toolboxes and activating conditions. If you have a lazy.xml file, no code will be loaded or compiled until it's actually needed.
How to do it?
1. Startup your extensions normally.
2. Go to base/cm/extension/util/lazy/generate.cm and run (replace fika with the name of your extension):
//generateLazyXmls("custom.fika");
this will generate lazy.xml files in the same directory as the extension.xml. Please note that this step can be skipped and in doing so will prevent the extension from becoming lazy.
3. Startup CET Designer again, you will now see a number of warnings mentioning UltraLazyExtension. For each extension that gets this warning, change so the extension inhertits from UltraLazyExtension (in cm.extension.util.lazy) instead of SimpleCatalogExtension or MultiCatalogExtension. The main point of converting to UltraLazyExtension is that you avoid having conflicting definitions of toolboxes etc. Lazy.xml will be the master at all times.
Expected result
On startup the extension might be snoozing (it is not fully loaded). It will only startup fully when:
1. The user selects the toolbox
2. You load a drawing that contains objects from the extension
3. Other conditions defined in lazy.xml
When that happens, a loading screen will popup showing that the extension is loading.
Note that in CET Build/Release mode, the system provides a initial grace period in which all extensions are free to start up fully (lazy threshold), in which after the time has passed, all remaining extensions not loaded will be snoozed. The default lazy threshold is 120 seconds.
Extension Startup Behaviour
Extension startup behaviour can fall under two categories.
- Non-Lazy startup behaviour.
- Lazy startup behaviour.
Non-Lazy startup behaviour applies to:
- Extensions that don’t have any lazy.xml (regardless of parent class).
- Extensions that use UltraLazyExtension but are defined with notLazy.xml.
- Extensions that define lazy.xml but are user-configured as a “favorite” via Control Panel.
- Extensions that define lazy.xml but are a dependency of another extension that requires to be non-lazy.
Lazy startup behaviour applies to:
- All other extensions (i.e. does not fall under any of the conditions above) that are defined with lazy.xml.
Refer to the section below for the difference in call order of your extension depending on the category it falls under
Startup Timeline
For a given example extension,
package custom.myExtension; public class MyExtension extends UltraLazyExtension { /** * Intialize. */ public void initialize(ExtensionEnv env) { //init code } /** * Start. */ public void start(ExtensionEnv env) { //start code } }
The difference in the startup timeline and the Extension method's call order is illustrated by the diagram below:
Automatic Unsnooze Phase (Conditional)
As per the timeline above, the final phase of the CET startup is the unsnoozed phase. The idea behind this is to give the user a favorable working state upon starting CET, tailored to each user.
Some lazy extensions will be identified and unsnoozed on CET startup. This phase is conditional based on a number of factors.
The important thing to note for your lazy extensions is that whether your extension will be unsnoozed automatically in this phase is non-deterministic. So do account for both cases (automatic and user-triggered)
Threshold
This phase is time lime limited by a threshold value. The default threshold is 30 seconds. If CET’s init and start phase are completed within this threshold value, the remaining time within the threshold will be used to perform unsnoozing of extensions.
If the CET’s init and start phase have exceeded the threshold value, there is no automatic unsnoozing at all. This is possible if the user has a slower machine and/or many extensions that fall under the Non-lazy startup category.
Extensions eligibility and unsnooze order
Only extensions that have started up snoozed (i.e. lazily started up extensions) are considered for automatic unsnooze.
If an extension startup has an average startup time of more than 10 seconds (based on statistics collected per machine), it will not eligible for automatic unsnoozing.
For all eligible extensions, the extensions will be ordered based on the user’s usage in previous sessions. More often used and recently used will be ranked higher in the unsnooze order. By default, all clicks of buttons and checkboxes from a toolbox library will be recorded as a user “action” for that extension.
You may add calls to the following function to record any additional actions that should be counted towards the usage statistics.
package custom.extension.ultraLazy; //ultraLazyStats.cm /** * Record action. */ public void ultraLazyRecordAction(symbol main) {
Note that extension dependencies are respected, hence it’s possible for a lazy extension to be unsnoozed automatically even if it is not ranked high enough, but instead due to it being a dependency of another extension which is being automatically unsnoozed.
User Configurability
A user may use the Control Panel’s Extension startup settings to select an extension to be excluded from being lazy, in which case that extension will follow the non-lazy startup behavior. This applies to all the dependencies of the extension as well.
Lazy.xml Reference
Lazy.xml is used to define the information needed at startup and its behaviors, with the root tag <lazy-extension-info>.
Lazy.xml structure reference:
- <main>
- attribute 'id' : Defines the package ID of the extension
- <quickBar> : Defines a TBPredefinedConfig for the Component Tab Quick Bar
- <id> : An custom identifier for the TBPredefinedConfig
- <label> : Display text for the TBPredefinedConfig
- <toolboxes> : Defines toolboxes
- <toolbox> : Defines a toolbox
- <package> : Package containing the toolbox function
- <function> : The name of the toolbox function
- <label> : Display text of the toolbox card
- <parent> : Grouping parent for collapsing toolbox cards
- <sortKey> : Sorting key for toolbox ordering
- <configId> : Which TBPredefinedConfig does it belong to?
- <visibility> : Use the value 'developMode' to make it visible in DEV only
- <channels> : Defines all the channels this toolbox is visible to
- <channel> : Name of the channel defined in MyConfigura with access to this toolbox
- <toolbox> : Defines a toolbox
- <triggers>
- <trigger> : Defines a trigger
- attribute id : Key of the trigger to be used with triggerLazyExtensions(key)
- <trigger> : Defines a trigger
- <keycodes>
- <keycode> : Defines a shortcut key registration at startup
- attribute id : Identifier of the shortcut
- attribute category : Category/Menu of the shortcut
- attribute key : The key press needed for the shortcut [A-Z0-9]
- attribute ctrl : Modifier Ctrl
- attribute alt : Modifier Alt
- attribute shift : Modifier Shift
- <keycode> : Defines a shortcut key registration at startup
- <resources>
- <resource> : Defines the resource files needed for toolbox card display labels
- attribute filename : File name of the RS file
- attribute pkg : Package containing the RS file
- attribute next : Next package to look for the RS entry if not found
- <resource> : Defines the resource files needed for toolbox card display labels
Lazy.xml Use Case Examples
Minimal
This is the minimal lazy.xml to make an extension lazy.
<?xml version="1.0" encoding="iso-8859-1"?> <lazy-extension-info> <main id="custom.developer" /> </lazy-extension-info>
Single Toolbox Extension
This is an example of a lazy.xml file for an Extension with a single toolbox. When the user clicks that toolbox icon, CET will build the toolbox by calling the "stairsLibrary" function. The parent defines how it should be grouped in the "Component Tabs Configurator".
To define a toolbox with:
- Package: custom.accessories.human
- Library function name: stdHumanLibrary
- Label: Humans (Note: Omit this if the label should be retrieved from rs files).
- Grouping parent: cet.accessories;dummy (Note: Will not create the parent container if it does not exist).
- Sorting key: custom.accessories.human
- Predefined toolbox key: std@predefined
<toolboxes> <toolbox> <package>custom.accessories.human</package> <function>stdHumanLibrary</function> <label>Humans</label> <parent>cet.accessories;dummy</parent> <sortKey>cet.accessories.human</sortKey> <configId>std@predefined</configId> </toolbox> </toolboxes>
Localized Toolbox Extension
This is the same toolbox, but now it will try to find the tooltip for the toolbox in the .rs file.
<?xml version="1.0" encoding="iso-8859-1"?> <lazy-extension-info> <main id="custom.accessories.human" /> <toolboxes> <toolbox> <package>custom.accessories.human</package> <function>stdHumanLibrary</function> <!-- FORCE TRANSLATION! <label>Humans</label> --> <parent>cet.accessories;dummy</parent> <sortKey>cet.accessories.human</sortKey> <configId>std@predefined</configId> </toolbox> </toolboxes> <resources> <!-- if we need to load resources early (flap name lookup). --> <resource filename="human.rs" next="cm.std" /> </resources> </lazy-extension-info>
Multiple Toolboxes
You can define multiple toolboxes for an extension.
<?xml version="1.0" encoding="iso-8859-1"?> <lazy-extension-info> <main id="custom.accessories.stairs" /> <toolboxes> <toolbox> <package>custom.accessories.stairs</package> <function>stairsLibrary</function> <!-- FORCE TRANSLATION! <label>Commercial Stairs</label> --> <parent>cet.buildings;dummy</parent> <sortKey>custom.accessories.stairs#000</sortKey> <configId>std@predefined</configId> </toolbox> <toolbox> <package>custom.accessories.stairs.industrialStairs</package> <function>indStairsLibrary</function> <!-- FORCE TRANSLATION! <label>Industrial Stairs</label> --> <parent>cet.buildings;dummy</parent> <sortKey>custom.accessories.stairs#001</sortKey> <configId>std@predefined</configId> </toolbox> </toolboxes> <resources> <!-- if we need to load resources early (flap name lookup). --> <resource filename="stairs.rs" /> <resource filename="indStairs.rs" pkg="custom.accessories.stairs.industrialStairs" next="custom.accessories.stairs" /> <resource filename="standards.rs" pkg="custom.accessories.stairs.standards" next="custom.accessories.stairs" /> </resources> </lazy-extension-info>
Toolbox Visibility
For toolboxes that are only visible when the user is in a specific channel or when CET is in developer mode. Channels are assigned to users through MyConfigura.
<toolbox> <package>custom.qaTools</package> <function>hiddenLibrary</function> <label>Hidden Library</label> <sortKey>cet.extensions.qa#9999</sortKey> <configId>custom.dev-all@predefined</configId> <visibility>developMode</visibility> <channels> <channel>custom.qaTools:custom:showHiddenToolbox</channel> </channels> </toolbox>
Component Tab Configuration
For defining component tab configuration. Should only be defined once per group in the "parent" extension. The <id> field is used to determine the members of the configuration by comparing with <toolboxes>/<toolbox>/<configId> of other extensions.
<?xml version="1.0" encoding="iso-8859-1"?> <lazy-extension-info> <main id="custom.developer" /> <quickBar> <id>custom.dev-all@predefined</id> <label>CET Developers</label> </quickBar> </lazy-extension-info>
Triggers
Sometimes you might want to activate the extension on certain actions or conditions.
For example, if your extension includes functionality to export the drawing, you want it to be activated if the user clicks "File -> Import & Export -> Export Drawing...". To do so, you need to add a trigger to your lazy.xml
<?xml version="1.0" encoding="iso-8859-1"?> <lazy-extension-info> <main id="custom.sketchUp" /> <triggers> <trigger id="sceneExport" /> </triggers> </lazy-extension-info>
SketchUp will now be activated whenever you call this method in your code:
triggerLazyExtensions("sceneExport");
Keycodes
Keycodes allow you to unsnooze the extension when a shortcut key is used.
<?xml version="1.0" encoding="iso-8859-1"?> <lazy-extension-info> <main id="custom.walkthrough" /> <triggers> <trigger id="toolsMenu" /> </triggers> <keycodes> <keycode id="wtLibrary" category="tools" key="M" ctrl="true" alt="true" /> </keycodes> </lazy-extension-info>
Complete Example
This example is based on a highly modified Stairs Library extension to better illustrate all of the available features of lazy.xml. This example is available in the base/lazy-reference-stairs branch.
<?xml version="1.0" encoding="iso-8859-1"?> <lazy-extension-info> # Extension package ID <main id="custom.accessories.stairs" /> # Define and register a TBPredefinedConfig with id custom.accessories.stairs-all@predefined" and label "Stairs". # Can be omitted if the same id is already registered somewhere else, e.g. in a mini startup Extension or the lazy.xml of a parent extension. <quickBar> <id>custom.accessories.stairs-all@predefined</id> <label>Stairs</label> </quickBar> # More than one toolbox can be defined in the lazy.xml. <toolboxes> # This defines a toolbox: * Package: custom.accessories.stairs * Library function name: stairsLibrary * Label: Commercial Stairs (Note: Omit this if the label should be retrieved from rs files). * Grouping parent: cet.buildings;dummy (Note: Will not create the parent container if it does not exist). * Sorting key: custom.accessories.stairs#000 * Predefined toolbox key: custom.accessories.stairs-all@predefined <toolbox> <package>custom.accessories.stairs</package> <function>stairsLibrary</function> <label>Commercial Stairs</label> <parent>cet.buildings;dummy</parent> <sortKey>custom.accessories.stairs#000</sortKey> <configId>custom.accessories.stairs-all@predefined</configId> </toolbox> <toolbox> <package>custom.accessories.stairs.industrialStairs</package> <function>indStairsLibrary</function> <label>Industrial Stairs</label> <parent>cet.buildings;dummy</parent> <sortKey>custom.accessories.stairs#001</sortKey> <configId>custom.accessories.stairs-all@predefined</configId> </toolbox> <toolbox> <package>custom.accessories.stairs</package> <function>devLibrary</function> <label>Dev Stairs</label> <parent>cet.buildings;dummy</parent> <sortKey>custom.accessories.stairs#002</sortKey> <configId>custom.accessories.stairs-all@predefined</configId> # Make a toolbox visible in developMode only <visibility>developMode</visibility> </toolbox> <toolbox> <package>custom.accessories.stairs</package> <function>hiddenLibrary</function> <label>Hidden Stairs</label> <parent>cet.buildings;dummy</parent> <sortKey>custom.accessories.stairs#003</sortKey> <configId>custom.accessories.stairs-all@predefined</configId> <visibility>developMode</visibility> # Make a toolbox visible only if subscribed to a channel <channels> <channel>custom.accessories.stairs:custom:showHiddenToolbox</channel> </channels> </toolbox> </toolboxes> # Allow this extension to be woken up by calling via cm.extension.ultraLazy.triggerlazyExtensions("stairs") <triggers> <trigger id="stairs" /> </triggers> # Allow this extension to be woken up through the shortcut CTRL+ALT+SHIFT+S <keycodes> <keycode id="stairsLibrary" category="tools" key="S" ctrl="true" alt="true" shift="true" /> </keycodes> # Resource files needed for toolbox labels if not using <label> in <toolbox> <resources> <resource filename="stairs.rs" /> <resource filename="indStairs.rs" pkg="custom.accessories.stairs.industrialStairs" next="custom.accessories.stairs" /> <resource filename="standards.rs" pkg="custom.accessories.stairs.standards" next="custom.accessories.stairs" /> </resources> </lazy-extension-info>
Tweaking Lazy Behaviour During Development
For development, developers may want to alter the default behaviour. Some examples would be
- Always force some extensions to follow non-lazy behaviour
- Skip the automatic unsnooze phase entirely to minimize entirely
- Always force an extension to be a candidate for the automatic unsnoozed phase for testing various cases where an extension can be stated.
This can be done by a debug XML file placed in the CET directory and applies to builds or developMode environment.
See cm/extension/ultraLazy/debug.md for more info
https://git.configura.com/cet/external/base/-/blob/trunk/13.5/cm/extension/ultraLazy/debug.md
Registering Data Catalogs
If you are registering embedded catalogs in your extension's initialize or start events, it may not run on CET startup. Instead, it will only run when your extension is unsnoozed (automatic or by the user's trigger). Quite likely you would want to register the catalogs on startup regardless of the snooze state of your extension. To fix this, you can embed your data catalogs within the extension.xml file by using the <embedded-catalogs> tag. This enables your data catalogs to be registered and downloaded without the need for your extension to unsnooze.
For more information on adding embedded catalogs to the XML file, refer to this guide:
extension.xml – Configura Help Center
FAQ
Q: What happens if I rename lazy.xml to notlazy.xml?
A: The extension will not be lazy, however, your toolbox will still be built according to the definitions in notlazy.xml.
Q: My extension is snoozing even though it's not extending from UltraLazyExtension!
A: As long as you have lazy.xml for that extension, it will become lazy.
Q: I get warnings like this: Warning! CollabExtension should be converted to UltraLazyExtension or it will CRASH in the release. Please refer to the migration section.
A: You need to migrate your extensions (that inherit SimpleCatalogExtension or MultiCatalogExtension), to instead inherit from UltraLazyExtension. See the guide on migrating to UltraLazyExtensions:
Q. Bugs in the customization init order
A: If there are Bugs in the customization init order, maybe it assumes all extensions are started at the same time. To check:
1. Start CET Designer with only one product line installed.
2. Go to extensions details, turn on all product lines.
3. Make sure that all toolboxes are shown correctly.
If this doesnt work, fix it before starting to make things lazy.
Q. My Extension cannot be lazy because it needs to do X during startup and X is not supported by lazy.xml
A: If you have some code that must run on startup, we recommend that you create a small startup extension that is not lazy, containing the code that must be executed. A typical example is that you might want to register the data catalogs to be downloaded.
/** * Extension class. */ private class FikaStartupExtension extends Extension { public void initialize(ExtensionEnv env) { // Do whatever things you need } } # Also please make sure the extension.xml has minimal dependencies. <?xml version="1.0" encoding="iso-8859-1"?> <extension-info> <name>Fika Startup</name> <version>9.9.6.0</version> <main>custom.fika.startup</main> <parent>custom.fika</parent> <packages> <package id="custom.fika.startup" /> </packages> <extension-dependencies> <dependency id="cm.core" /> </extension-dependencies> <security-delegates> <delegate id="custom.fika" /> </security-delegates> </extension-info>
Q: My extensions generates toolboxes dynamically, so I cant list them down in lazy.xml
A: In some rare cases where you would dynamically generate a number of toolboxes (sometimes 1, sometimes 10), you can use AdvancedToolboxExtension instead.
Q: It's crashing! It must be because it's lazy.
A: Try renaming the lazy.xml file to notlazy.xml (only available in 10.5 so far). Toolboxes will still be built from the XML spec, but the extension will not be lazy.
Q: Now everything is started in the wrong order! It must be because it's lazy.
A: If you have any init-order issue, we recommend that you put the init code in Extension.initialize() or Extension.start() rather than keep it in init-blocks that run seemingly more random.
Q: Some of my Extensions are UltraLazyExtension and some of my Extensions are not (e.g. AdvancedToolboxExtension) and the TBPredefinedConfig doesn't work properly for those non-lazy extensions?
A: During the startup of those non-lazy extensions, you could use getPredefinedToolboxConfig with the key of the <quickBar> defined in one of the lazy.xmls of the lazy extensions, to obtain the TBConfig object, which you can then append TBInfo of non-lazy extensions to.
Comments
0 comments
Please sign in to leave a comment.