-
Notifications
You must be signed in to change notification settings - Fork 0
bundle
io.bundle
provides high-level tools to orchestrate I/O. It bundles several requests in one transparently:
- A bundle is transferred as one, receives a response, which is unbundled transparently too. Users should not modify their code.
- Bundling can be done automatically using a time window, e.g., 40 milliseconds, or manually.
- All current subscribers for a resource (who submitted
io()
requests for it) will be notified on unbundling.- Additionally results can be cached (see cache service), so future requests can be satisfied immediately.
- It provides a foundation to request resources ahead of time.
- User can request a resource and supply an additional bunch for related resources, which can be cached for future use.
- A simple request can return a bundle, which will be stored in the cache for future use.
- Pre-fetching: provisions are made to facilitate data requests before loading any JavaScript libraries to improve web pages' time-to-be-useful greatly.
In order to use bundling, a simple server-side code is required. A reference implementation in JavaScript is provided by heya-bundler. If you want to implement it in a different language, or an environment, take a look at protocol spec.
The rationale of bundling discussed in Why bundle?
Don't forget to consult the cookbook to see working snippets solving simple real-world problems.
This service requires track to be active. If used with cache, it can save bundled results for future use. In general, cache is a recommended companion service.
If options
object has a Boolean property bundle
, it tells the bundle service to process or to ignore a request. Otherwise, a default algorithm is used described in Service documentation.
The following properties are available to customize all aspects of the advanced I/O handling.
This function starts collecting a bundle: all eligible requests will be queued until io.bundle.commit()
is called.
This function finishes collecting a bundle: all collected requests are assembled in bundles and sent to a bundler.
This function returns a Boolean value: true
if we are collecting a bundle now, false
otherwise.
This property defines a number of milliseconds to wait before issuing any I/O requests and collect a bundle. Default: 20.
If it is 0, no time-based bundle collection takes place. Any other value causes the following behavior:
- The first I/O request that is eligible for bundling (see
io.bundle.canBeBundled()
) issuesio.bundle.start()
, which places it in a queue. - A timer is started for
io.bundle.waitTime
milliseconds. - While the timer is active, all eligible I/O requests go to the queue.
- When the timer is expired,
io.bundle.commit()
is issued, all collected requests are properly bundled, and issued.
Important: the bigger io.bundle.waitTime
, the more I/O requests can be collected for bundling. But remember to avoid pitfalls:
- The bigger the wait time, the bigger the delay before issuing a bundle. For example, if we wait for 10 seconds, it means that our first I/O request will be delayed by 10 whole seconds. It is very likely that instead of a speedup we got ourselves a massive delay.
- Usually an application issues a limited amount of I/O requests before staring to wait for responses. It means that starting from a certain moment increasing the wait time will fail to collect more requests slowing down our application needlessly.
In many cases we want to collect requests made during a current thread. If this is a case, consider issuing io.bundle.start()
and io.bundle.commit()
with heya-defer
facilities (see the Cookbook: bundle).
This property defines a URL where to send bundles. Default: '/bundle'
.
This property defines a minimal bundle size as a number. Any bundle less than this number will be disassembled, and all requests will be issued individually as is. Default: 2.
The default value bundles 2 requests and more together but sends individual requests as is. If we want to send all requests as bundles, we can set it to 1.
This property defines a maximal bundle size as a number. If we have more requests, they will be sent out in different bundles up to the maximal size. Default: 20.
Why do we need an upper limit on bundles? Server-Side implementation of a bundler implements an upper limit for security considerations: even if we can verify a source of bundles, having no limit, or very high limit, opens us to a DOS attack. While DOS is possible with regular requests, and we know how to handle those, a bundler can serve as a multiplier, and requires special care like putting an upper limit on a bundle.
Keep in mind that io.bundle.maxSize
should correspond to an actual restriction on a server-side. While we can send bigger bundles, they can be rejected by a server, and we don't want that.
This function receives a data object as a single argument and returns an array of responses for unbundling, or null
.
The default implementation does the following actions:
- Checks if it is non-
null
object. - Checks if it complies to the bundle protocol:
- Checks if there is a property named
bundle
, which value is'bundle'
. - Checks if there is a property named
results
, and it is an array.
- Checks if there is a property named
- If everything checks out, it returns
results
property. - Otherwise,
null
is returned.
This function is related to unbundling. It returns no value and takes one argument: a data object. That data object is checked with io.bundle.detect()
, and if it recognized as a bundle response, it is unbundled, all waiting subscribers are notified, and results are cached for future use, if [cache}(cache) is used.
It is a helper. This procedure submits a bundle. It returns no value, and takes a single argument: an array of options
.
If we already collecting a bundle (see io.bundle.isStarted()
above), it adds to a bundle. Otherwise it opens a bundle with
io.bundle.start()
, submits all options
, and commits a bundle immediately with io.bundle.commit()
.
This helper submits several I/O requests, yet return no promises. It is assumed that individual requests will be made before the bundle is in flight (see track), or their results will be cached (requires cache) for future use.
It is a helper. This function submits a request with a related bundle. It takes two arguments:
-
options
— the standardoptions
object for a main request. -
bundle
— an array ofoptions
for a supplemental bundle.
It returns a promise for the main request. It is assumed that individual requests will be made before the bundle is in flight (see track), or their results will be cached (requires cache) for future use.
This procedure is related to pre-fetching. It registers a bundle as being in progress without initiating actual requests. It returns no value, and takes one argument: a bundle as an array of options
.
See the Cookbook: bundle for examples of use.
This is a dictionary object, which collects bundle requests. Keys are unique keys made by io.makeKey(options)
or io.prepareRequest(options)
(see the main API). Values are objects with following properties:
-
options
— an originaloptions
object. -
prep
— an object produced byio.prepareRequest(options)
(see the main API). -
level
— an index number of the bundle service.
Essentially all three properties are copies of the bundle service parameters. See Service for more details.
See Service documentation for more details.