Greetings,
Below is the first draft of a proposal for the adoption of a new catalog
format for the client and server. Substantive feedback is requested by
COB of Thursday, July 23rd 2009:
pkg(5): image packaging system
CATALOG FORMAT AND CACHING PROPOSAL
1. Overview
The pkg(5) server and client catalogs currently provides a summary
view of the packages provided by a repository: the FMRIs of each
package, the last time the set of available packages changed, and
the total number of packages. The server uses this information
for publication checks, to fulfill client requests, for search
indexing and analysis, and to enable browser-based access to the
repository via the BUI (Browser User Interface). pkg(5) clients
use this information to determine what packages are available, to
validate user input, and to fulfill packaging operation requests.
1.1 History
As development of the Image Packaging System has progressed, both
the server and client have increasingly required access to more
packaged metadata as fixes and various improvements have been
implemented. This has resulted in increased demand on server and
client system resources when analyzing package metadata, and
increased processing times as well.
To address catalog performance issues, a client-side unified
catalog cache was implemented, and initially contained all known
package stems from the set of publishers defined within the image
configuration. The caching mechanism was then replaced, using a
Python dict structure designed for efficient lookups of package
information by stem or FMRI and providing an ordered list of
versions, that was then serialized to disk.
Recently, the caching was revised to use a custom, delta-encoded
text format that avoided object serialization as that created an
implicit dependency on object versions and definitions, as well as
significant overhead in the on-disk footprint. To improve package
metadata performance, a new cache format was created that factored
package manifests by the types of actions contained within, and
then stored each type of action in a separate file for each
manifest.
1.2 Challenges
Despite past improvements, significant performance improvements
are still needed for both the server and client when processing
and analyzing package metadata. The work done so far has also
only benefited the client, leaving server performance behind.
Specifically, the underlying catalog data, caching mecahnisms,
and catalog retrieval operations suffer from the following
issues:
- the catalog format used for the server and client is not
consistent and the server uses local time instead of UTC
- the client does not maintain a 1:1 copy of the server's catalog
and attributes making it difficult to verify its integrity and
complicates the logic needed to obtain updates
- the caching mechanisms implemented are not granular enough,
causing some operations to run slower than necessary as more
information than is needed is loaded and processed
- no efficient lookup mechanism exists for some of the metadata,
causing operations such as dependency calculation to require a
linear scan and retrieval of manifests
- the existing caching mechanisms require clients to retrieve
manifests for all known packages to be able to perform summary
listings of available packages (at least 65 MiB for a new build
of OpenSolaris) -- which is especially harmful to GUI clients
such as packagemanager(1)
- the existing caching mechanisms do not provide the information
needed to determine (ahead of time) what package manifests need
to be retrieved during packaging operations, which leaves pkg(5)
clients unable to provide sufficient feedback to the user during
plan creation such as number of bytes to be transferred, time
estimates, etc.
- the catalog operation and caching mechanisms offered by the
depot server are not extensible, and cannot accommodate new
metadata that may be needed to perform client operations
without a client and server revision
- the catalog and caching mechanisms do not account for
future localization needs
1.3 Goals
So then, the changes proposed within this document have the
following goals:
- unification of the server and client catalog format and code
- simplification of catalog update and retrieval mechanisms
- improved granularity and transparency in caching mechanisms
allowing operations to only retrieve the information they need
- reduction of resource requirements and processing time forserver
and client
- increase of available metadata before long-running package
operations to enable improved progress and user feedback
- improved extensibility of the catalog depot operation and the
caching mechanisms used by the client
- unification and implementation of caching mechanisms and code
for client and server
2. Proposed Changes
The changes needed to accomplish the goals listed in section 1.3
are grouped below by the type of change. It should be noted that
what is described in this document is dependent on an upcoming image
and repository format versioning proposal since these changes will
require a change to the structure of both images and repositories.
2.1 Catalog Format Changes
2.1.1 Current Catalog Format
To better understand the proposed changes, it may first be helpful
to understand the current catalog format and how it is composed.
Currently, the catalog could be viewd as being composed of three
files:
- attrs
The attrs file contains metadata about the catalog. The
server and client attrs file are text/plain, and currently
have the following content:
S Last-Modified: 2009-06-23T07:58:35.686485
S prefix: CRSV
S npkgs: 40802
The client adds this content:
S origin: <repository_uri>
The Last-Modified value is an ISO-8601 date and time in server
local time (not UTC).
- catalog
The server catalog file currently contains entries of this
format:
<type> <fmri><newline><newline>
Where type can be 'V' (version), 'C' (critical; not used), or
'R' (renamed).
As a special exception, the format of 'R' entries is:
R <src_stem> <src_version> <dest_stem> <dest_version><newline>
If a destination package is not provided for 'R', then 'NULL'
is used for the destination values.
Examples:
C pkg:/[email protected],5.11-0.111:20090507T161015Z
V pkg:/[email protected],5.11-0.111:20090508T161015Z
R foo 1.0:20090508T161015Z bar 1.0:20090509T161015Z
R baz 1.0:20090508T161015Z NULL NULL
The client catalog file contains entries of this format:
<type> pkg <fmri_stem> <fmri_version><newline>
As a special exception, the format of 'R' entries is:
R <src_stem> <src_version> <dest_stem> <dest_version><newline>
If a destination package is not provided for 'R', then 'NULL'
is used for the destination values.
Example:
V pkg foo 0.5.11,5.11-0.111:20090508T161015Z
- updatelog
While not strictly a part of the catalog, the updatelog serves
as a record of changes to the catalog allowing clients to
obtain incremental updates to a catalog instead of retrieving
the entire catalog each time.
It only exists on the server, and contains entries of this
format:
<update_type><type><space><fmri><newline>
Where 'update_type' can be '+' (there were comments at one
time referring to a '-' operation, but they were removed and
the code for it was never implemented).
Where 'type' can be 'V' (version), 'C' (critical; not used),
or 'R' (renamed).
As a special exception, the format of 'R' entries is:
R <src_stem> <src_version> <dest_stem> <dest_version><newline>
2.1.2 Proposed Catalog Format
To accomplish the goals listed in section 2.1, a new catalog
format will be adopted. This format will be used by the client
to store catalog data locally, regardless of the format used by
the repository (e.g. the repository only provides older catalog
format). Catalogs are not separated by locale since manifests are
not, and all data is assumed to be encodable using UTF-8.
Catalogs using this format will be composed of the following files:
- catalog.attrs.<name>
This file will contain a python dict structure serialized in
JSON (JavaScript Object Notation) format. The metadata within
is used to describe the catalog file and its contents using the
following attributes:
created:
The value is an ISO-8601 formatted date in UTC time
indicating when the catalog was created.
last-modified:
The value is an ISO-8601 formatted date in UTC time
indicating when the catalog was last updated.
package-count:
An integer value indicating the total number of unique
FMRI stems in the catalog.
package-version-count:
An integer value indicating the total number of unique
FMRI versions in the catalog.
update-logs:
A list of dates indicating update logs available for
catalogs for clients that support incremental catalog
updates.
version:
An integer value representing the version of the
structure used within the JSON file.
Example:
{
'created': '2005-06-14T08:00:00.686485',
'last-modified': '2009-05-08T16:10:25.686485',
'package-count': 40802,
'unique-package-count': 1706,
'update-logs': ['2008100208', '2009050816'],
'version': 1,
}
- catalog.<name>
Catalog files will contain a python dict structure serialized
in JSON (JavaScript Object Notation) format. Version entries
for each package stem are kept in ascending version order to
allow fast lookups by the client and avoid sort overhead on
load. The structure can be described as follows:
{
<publisher-prefix>: {
<FMRI package stem>: [
{
"op-time": <ISO-8601 Date and Time>
"version": <FMRI version string>,
<optional-actions>: <optional-actions-data>
}
]
}
}
Initially, the server will offer the following catalogs. Each has
its content based on a tradeoff between memory usage, loading times,
and bandwidth needs which depend on the client being used to perform
packaging operations or the operation being performed.
- catalog.base
This catalog file only contains the FMRIs of the packages that
the repository contains. Loading just this catalog is useful
when performing basic listing operations using the cli, or when
simply checking to see if a given package FMRI is valid. An
example is shown below:
{
"opensolaris.org":{
"SUNWipkg":[
{
"version":"0.5.11,5.11-0.117:20090623T135937Z"
},
{
"version":"0.5.11,5.11-0.118:20090707T220625Z"
}
],
"SUNWsolnm":[
{
"version":"0.5.11,5.11-0.117:20090623T144046Z"
},
{
"version":"0.5.11,5.11-0.118:20090707T224654Z"
}
]
}
}
- catalog.dependency
This catalog file contains the FMRIs of the packages that the
repository contains, any 'depend' actions, and any 'set' actions
for facets or variants. This information is intended to be used
during dependency calculation by install, uninstall, etc. An
example is shown below:
{
"opensolaris.org":{
"SUNWdvdrw":[
{
"version":"5.21.4.10.8,5.11-0.108:20090218T042840Z",
"actions":[
"set name=variant.zone value=global value=nonglobal",
"set name=variant.arch value=sparc value=i386",
"depend [email protected] type=require",
"depend [email protected] type=require",
"depend [email protected] type=require"
]
}
],
"SUNWthunderbirdl10n-extra":[
{
"version":"0.5.11,5.11-0.75:20071114T205327Z",
}
]
}
}
- catalog.summary
This catalog file contains the FMRIs of the packages that the
repository contains and any 'set' actions (excluding those for
facets or variants). This information is intended to be used
primarily by GUI clients such as packagemanager(1), or the BUI
(Browser UI) provided by pkg.depotd(1m) for quick, efficient
access to package metadata for listing. An example is shown
below:
{
"opensolaris.org":{
"SUNWdvdrw":[
{
"version":"5.21.4.10.8,5.11-0.108:20090218T042840Z",
"actions":[
"set name=description value=\"DVD creation utilities\"",
"set name=info.classification
value=org.opensolaris.category.2008:System/Media",
]
}
],
"SUNWthunderbirdl10n-extra":[
{
"version":"0.5.11,5.11-0.75:20071114T205327Z",
"actions":[
"set name=description value=\"Thunderbird
localization - other 15 lang\"",
"set name=srcpkgs value=SUNWthunderbirdl10n-extra"
]
}
]
}
}
To enable incremental catalog updates, an "updatelog" will also be
provided as a single, merged file that can be used to incrementally
update any of the catalogs. It is composed of the following files:
- updatelog.attrs.<logdate>
This file will contain a python dict structure serialized in
JSON (JavaScript Object Notation) format. The metadata within
is used to describe the updatelog file and its contents using
the following attributes:
created:
The value is an ISO-8601 formatted date in UTC time
indicating when the updatelog was created.
last-modified:
The value is an ISO-8601 formatted date in UTC time
indicating when the updatelog was last updated.
update-count:
An integer value indicating the total number of updates
in the updatelog.
version:
An integer value representing the version of the
structure used within the JSON file.
Example:
{
'created': '2005-06-14T08:00:00.686485',
'last-modified': '2009-05-08T16:10:25.686485',
'update-count': 2,
'version': 1,
}
- updatelog.<logdate>
This file will contain a python dict structure serialized in
JSON (JavaScript Object Notation) format. <logdate> is a UTC
date and time of the format YYYYMMDDHH.
The structure of catalog update files is nearly identical to
that of catalog files, with a few exceptions. Specifically,
each version entry contains additional elements indicating
the catalog operation, the time of the operation, and any
actions that are offered by the current catalogs for that
package if applicable to the operation.
The supported types (<op-type> in the example below) of catalog
operations are:
'add' Indicates that the corresponding FMRI and metadata
(if present) has been added to the catalog.
'remove' Indicates that the corresponding FMRI has been
removed from the catalog.
The structure can be described as follows:
{
<publisher-prefix>: {
<FMRI package stem>: [
{
"op-type": <type-of-operation>
"op-time": <ISO-8601 Date and Time>
"version": <FMRI version string>,
<optional-actions>: <optional-actions-data>
}
]
}
}
An example updatelog might consist of the following:
{
"opensolaris.org":{
"SUNWthunderbirdl10n-extra":[
{
"op-type": "remove",
"op-time": "20090218T042838Z"
"version":"0.5.11,5.11-0.75:20071114T205327Z",
}
],
"SUNWdvdrw":[
{
"op-type": "add",
"version":"5.21.4.10.8,5.11-0.111:20090524T042840Z",
"actions":[
"set name=description value=\"DVD creation utilities\"",
"set name=info.classification
value=org.opensolaris.category.2008:System/Media",
"depend [email protected] type=require",
"depend [email protected] type=require",
"depend [email protected] type=require"
]
}
]
}
}
For a detailed discussion on the choice of data format and a
performance analysis, see section 3.
2.2 Server Changes
To enable clients to retrieve the new catalog files and incremental
updates to them, the following changes will be made:
- The new catalog files will be stored in the /var/pkg/catalog
directory using the filenames described in section 2.1.2. Any
existing catalog files will be converted to the new format upon
load (using writable-root if present) and the old ones removed
(unless readonly operation is in effect).
- Operations that modify the catalog file will be changed to write
out all of the new catalogs only; the version 0 catalog will no
longer be stored or used.
- The depot server will be changed to offer an additional catalog
operation "/catalog/1/" which will be added to the output of the
"/versions/0/" operation as well. It will provide a simple GET-
based HTTP/1.1 interface for retrieving catalog and updatelog
files from the server. It will not require or use any headers
other than those normally present within a standard HTTP/1.1
transaction. However, the client api will continue to provide
the uuid, intent, and user agent headers that are provided today
for the existing "/catalog/0/" operation.
- The existing "/catalog/0/" operation will continue to be offered
by the depot server for compatibility with older clients.
- The depot server will be changed to perform a simple sanity check
when starting to verify that the packages in the catalog are
physically present in the repository and that the catalog attrs
files match the catalog files. Likewise, the updatelogs will also
be checked to verify that they are valid for the catalogs. If
any of these files are found to be not valid, a warning will be
logged and the catalog files rewritten (using writable-root if
applicable). In addition, any of the corrections made will
result in corresponding updatelog entries so that incremental
updates will not be broken for existing clients.
2.3 Client Changes
To take advantage of the new catalog format, and to improve the
performance of clients, a number of changes will need to be made
to the pkg.client.api and its supporting classes. All of the
changes proposed here should be transparent to client api
consumers.
2.3.1 Image Changes
- The image object, upon initialization, will remove the
/var/pkg/catalog directory and its contents if possible.
If this cannot be done (due to permissions), the client
will continue on. If it can be removed, a new directory
named /var/pkg/publisher be created, and publisher objects
will be told to store and retrieve their metadata from it.
- Any functions contained within the image class for the
direct storage, retrieval, updating, etc. of publisher
metadata will be moved to the pkg.client.publisher and
Catalog classes.
- A new "Catalog" object reference will be added to the
image class, which will be used to allow the api access
to catalog metadata. This object will allow callers to
ask for a specific set of catalog data for an operation
(where the allowed sets match the names of the catalogs
described in section 2.1.2). The data will then be
retrieved and stored for usage by callers as needed.
- The existing catalog caching mechanism will be removed
completely as it has been superseded by the new catalog
format.
- For performance reasons, the client api will also store
versions of each of the catalogs proposed that only
contain entries for installed FMRIs to accelerate common
client functions such as info, list, uninstall, etc.
- All api functions will be changed to retrieve the catalog
data they need instead of depending upon api consumers to
do so.
2.3.2 Catalog Retrieval and Update Changes
- If a repository only offers version 0 of the catalog format,
then the client API will retrieve it, but transform and store
the catalog in version 1 format.
- If version 1 catalog data is not available, the client api will
fallback to retrieving catalog metadata by retrieving package
manifests (as it does today). This will be transparent to
clients.
- When checking for catalog updates, the client api(s) will follow
this process for version 1 catalogs when determining if a full or
incremental update should be performed for each catalog in the
image:
* if the repository now offers a version 1 catalog, but did not do
so previously, a full catalog retrieval will be performed
* the attrs file for the catalog will be checked to see if it
has been modified by asking the repository. If a not modified
status is returned (304) then no updates are available and the
catalog will be skipped during the update process. If a new
attrs file is available, it will be retrieved.
* if the attrs file has been retrieved, its corresponding catalog
file will be checked as follows:
- If the created date in the retrieved attrs file does not
match the stored attrs file, a full catalog retrieval will be
performed as the catalog has been rebuilt. In addition, a
warning will be provided to the client that there may be
something wrong with the repository (packages may be missing,
etc.).
- If the created date matches, then the version in the new attrs
file will be compared to the original, if they do not match a
full catalog retrieval will be performed as the format of the
catalog has changed (unless the client is unable to parse that
format in which case an error will be raised).
- If the version was valid, then the last modified date in the
new catalog attrs file will be compared to the original attrs
file. If the original attrs date is newer, then a full
catalog retrieval will be performed and the user will be
warned that there may be something wrong with the repository
(packages may no longer be available, etc.). If the last
modified date in the original attrs file is the same as the
new attrs file, then no updates are available and the catalog
will be skipped. If the original attrs last modified date is
older than the new attrs last modified date, then the 'update-
logs' property will be checked to see if there are incremental
updates available.
- If the update-logs property is empty, a full catalog retrieval
will be performed with the assumption that the repository has
intentionally discarded all of its incremental update
information. If the oldest updatelog listed in the new attrs
file is newer than the last modified date of the original
attrs file, then this client has not performed an incremental
for a period long enough that the repository no longer offers
incremental updates for their version of the catalog, and a
full catalog retrieval will be performed.
- Finally, if all of the above was successful, the api will then
start the incremental update process.
- When attempting to determine what incremental catalog updates
for version 1 catalogs are available, and the repository offers
version 1 catalogs, the client api(s) will use the following
process:
* the attrs file for the updatelog will be checked to see if it
has been modified by asking the repository. If a not modified
status is returned (304) then no updates are available and the
updatelog will be skipped during the update process. However,
if this was the last updatelog listed, then a full catalog
retrieval will be performed and the user will be warned that
an incremental update could not be performed due to problems
with the repository. If the attrs file has been modified,
then it will be retrieved.
* if the attrs file has been retrieved, its corresponding
updatelog file will be checked as follows:
- If the created date in the retrieved attrs file does not
match the stored attrs file, a full catalog retrieval will
be performed as the catalog has been rebuilt and the updatelog
is no longer valid even though the logdate matches. In
addition, a warning will be provided to the client that there
may be something wrong with the repository (packages may be
missing, etc.).
- If the created date matches, then the version in the new attrs
file will be compared to the original, if they do not match,
a full updatelog retrieval will be performed as the format of
the updatelog has changed (unless the client is unable to
parse that format in which case an error will be raised).
- If the version was valid, then the last modified date in the
new updatelog attrs file will be compared to the original
attrs file. If the original attrs date is newer, then a full
catalog retrieval will be performed and the user will be
warned that there may be something wrong with the repository
(packages may no longer be available, etc.). This is done as
the date change indicates that the existing updatelog is no
longer valid and an incremental update cannot be safely
performed.
- If the original attrs last modified date is older than the
new attrs last modified date, then the corresponding
updatelog file will be retrieved. If the last modified date
in the new attrs file exactly matches the the original
updatelog attrs file, a full catalog retrieval will be
performed as the catalog has been rebuilt and the updatelog
is no longer valid even though the logdate matches. In
addition, a warning will be provided to the client that
there may be something wrong with the repository (packages
may be missing, etc.).
- Finally, if all of the above was successful, the retrieved
updatelog will be added to the update queue.
- When applying the queued catalog updates, the client api will
use this process for each updatelog:
* each corresponding catalog present in the image will be
loaded, and then any updatelog entries newer than the last
modified date of the catalog (based on op-time) will be
applied to the catalog as dicated by op-type
* if at any point, an updatelog entry cannot be applied as
directed, then a full catalog retrieval will be forced, and
the user will be warned that something may be wrong with the
repository (missing packages, etc.)
* if the updatelog is the last in the queue for a given set of
catalogs, then all previous ones will be removed as they are
no longer needed
3. Appendix
3.1 Overview
During the development of this proposal, a number of different
approaches to the storage and retrieval of catalog data were
considered. Specifically, the following formats were considered
and/or evaluated:
- manifest
A pure "manifest-style" format similar to the existing package
manifest.
- JSON
The portable JavaScript Object Notation-based format.
Size and performance characteristics for each of these formats can
be found in section 3.3.
3.2 Evaluations
3.2.1 manifest-style format evaluation
Initially, the "manifest-style" format seemed promising from a
performance and disk footprint standpoint when compared to using
JSON. A few variations of this format were attempted, and examples
of this can be seen below:
- variation 1
pkg://opensolaris.org/[email protected],5.11-0.86:20080422T230436Z
[email protected],[email protected]
- variation 2
pkg://opensolaris.org/[email protected],5.11-0.86:20080422T230436Z
depend fmri=pkg:/[email protected] type=require
depend fmri=pkg:/[email protected] type=require
- variation 3
After realising that variant and facet information was needed,
and that additional attributes might need to be accounted for in
the future, variation 3 was chosen for evaluation.
pkg://opensolaris.org/[email protected],5.11-0.106:20090131T184044Z
set name=variant.zone value=global value=nonglobal
set name=variant.arch value=sparc value=i386
depend [email protected] type=require
depend [email protected] type=require
3.2.2 JSON format evaluation
When first evaluating JSON, results on x86-based systems were very
comparable or significantly better than the manifest-based format
from both a file size and performance perspective. The following
structural variations were evaluated:
- variation 1
Variation one attempted to combine the catalog and attrs files,
but this approach was abandoned for simplicity and performance
reasons in later variations.
{
"attributes": {
"id": "556599b2-aae8-4e67-94b3-c58a07dbd91b",
"last-modified: "2009-05-08T16:10:25.686485",
"locale": "C",
"package-count: 40802,
"version: 1,
},
"packages": {
"SUNWipkg": {
"publisher": "opensolaris.org",
"versions": [
"0.5.11,5.11-0.111:20090331T083235Z",
"0.5.11,5.11-0.111:20090418T191601Z",
"0.5.11,5.11-0.111:20090508T161025Z",
],
},
},
}
- variation 2
{
"packages":{
"SUNWlang-cs-extra":{
"publisher":"opensolaris.org",
"versions":[
[
"0.5.11,5.11-0.86:20080422T230436Z",
{
"depend":{
"require":[
{
"fmri":"foo"
},
{
"fmri":"bar"
}
],
"optional":[
{
"fmri":"baz"
},
{
"fmri":"quux"
}
]
}
}
],
]
}
}
}
- variation 3
This variation was attempted due to extreme performance issues
that were seen on some lower-memory bandwidth SPARC systems
when writing JSON files. It was discovered that the simplejson
library uses a recursive call structure for iterative encoding
of python data structures and this does not perform well on many
SPARC systems.
By changing the structure to a list of lists, a decrease om
write times of 20-30 seconds was realised. However, this was
less than desirable as it meant the resulting data structure
would have to be significantly tranformed after load for use
by the package system.
[['pkg://opensolaris.org/[email protected],5.11-0.111:20090508T163711Z'],
['pkg://opensolaris.org/[email protected],5.11-0.99:20081002T152038Z',
[['require',
['[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]']]]]
]
- variation 4
This variation was struck upon after the failure of the last
with the attempt to have a data structure that was immediately
useful to the packaging system after load:
{
'opensolaris.org': {
'SUNWsongbird': [
{
'depend': {
'require': [
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]'
]
},
'version': '0.5.11,5.11-0.99:20081002T152038Z'
},
],
'SUNWstc': [
{
'version': '0.5.11,5.11-0.106:20090131T191239Z'
},
],
},
}
- variation 5
The final variation is what was chosen for final evaluation for
JSON after discussions with other team members centered around
a key point: that the catalog is essentially an action pipeline
for the client. In addition, the prior variations were either
hampered by poor serialization performance on SPARC systems or
lacked the extensibility needed for possible future attribute
additions to actions.
{
"opensolaris.org":{
"SUNWdvdrw":[
{
"version":"5.21.4.10.8,5.11-0.108:20090218T042840Z",
"actions":[
"set name=description value=\"DVD creation utilities\"",
]
}
],
}
}
3.2.3 Performance Analysis
While a performance analysis was done for each variation during the
evaluation process, only the results for the chosen variation are
shown here. Analyis was performed using a dump of the /dev repo
for builds 118 and prior consisting of 42,565 unique FMRIs.
Each format that was evaluated presented unique challenges. While
the manifest-style provided simplicity and familiarity, it became
increasingly apparent during testing that any code that was used
to parse and write it would have to be changed significantly each
time changes were made to any in-memory structures that were used
as the source. In contrast, the JSON format made it easy to re-use
the in-memory python structure as the same format to be written to
disk.
The uncompressed and gzip-compressed (provided because both Apache
and cherrypy are capable of gzip compressing requests) are shown
below for comparison. Of special note is the 'all' catalog shown
below which was created to evaluate the feasbility of having a
single catalog that provided access to all commonly needed metadata
by combining the base, dependency, and summary catalogs proposed in
section 2.1.2.
=================================================================
Size Comparison
=================================================================
Catalog Mfst. Sz. JSON Sz. Mfst. CSz. JSON CSz.
-----------------------------------------------------------------
current 2.25 MiB - 327 KiB -
base 2.86 MiB 2.00 MiB 305 KiB 246 KiB
dependency 16.44 MiB 16.45 MiB 1.4 MiB 1.4 MiB
summary 7.58 MiB 7.36 MiB 483 KiB 475 KiB
all 21.16 MiB 21.47 MiB 1.6 MiB 1.6 MiB
The time needed to read and write each format is shown below for
comparison. Several runs for each catalog were performed to verify
that the timings were consistent, and the load of each system was
checked to verify that timings were not skewed.
=================================================================
Base Catalog Timings
=================================================================
System Mfst. Wr. JSON Wr. Mfst. Rd. JSON Rd.
-----------------------------------------------------------------
mine 0.13s 0.41s 0.19s 0.05s
ipkg.sfbay 0.19s 0.58s 0.29s 0.08s
kodiak.eng 0.30s 0.99s 0.37s 0.08s
cuphead.sfbay 1.18s 3.41s 1.54s 0.33s
jurassic.eng 1.37s 3.77s 1.31s 0.46s
-----------------------------------------------------------------
Mean 0.63s 1.83s 0.74s 0.20s
=================================================================
Dependency Catalog Timings
=================================================================
System Mfst. Wr. JSON Wr. Mfst. Rd. JSON Rd.
-----------------------------------------------------------------
mine 0.42s 1.06s 1.13s 0.24s
ipkg.sfbay 0.98s 1.65s 1.70s 0.39s
kodiak.eng 0.91s 2.61s 2.22s 0.40s
cuphead.sfbay 6.05s 9.00s 8.57s 1.57s
jurassic.eng 3.87s 10.46s 6.48s 2.13s
-----------------------------------------------------------------
Mean 2.45s 4.96s 4.02s 0.95s
=================================================================
Summary Catalog Timings
=================================================================
System Mfst. Wr. JSON Wr. Mfst. Rd. JSON Rd.
-----------------------------------------------------------------
mine 0.16s 0.78s 0.58s 0.14s
ipkg.sfbay 0.33s 1.09s 0.86s 0.22s
kodiak.eng 0.35s 1.90s 1.10s 0.25s
cuphead.sfbay 2.02s 6.55s 4.41s 0.92s
jurassic.eng 1.35s 7.24s 3.34s 1.25s
-----------------------------------------------------------------
Mean 0.84s 3.51s 2.06s 0.56s
=================================================================
'all' Catalog Timings
=================================================================
System Mfst. Wr. JSON Wr. Mfst. Rd. JSON Rd.
-----------------------------------------------------------------
mine 0.51s 1.22s 1.48s 0.31s
ipkg.sfbay 1.22s 1.89s 2.30s 0.51s
kodiak.eng 1.09s 3.05s 2.93s 0.53s
cuphead.sfbay 7.35s 10.38s 11.15s 2.02s
jurassic.eng 4.57s 12.20s 8.28s 2.74s
-----------------------------------------------------------------
Mean 2.95s 5.75s 5.23s 1.22s
System Notes:
- 'mine' is an Intel Core 2 DUO E8600 with 8GiB RAM
- ipkg.sfbay is a dual Opteron 2218 with 16GiB RAM
- kodiak.eng is a SPARC64-VI box with 32GiB RAM
- cuphead.sfbay is an UltraSparc-T2 with 3GiB RAM
(likely ldom or zone)
- jurassic.eng is an UltraSPARC-III+ with 32GiB RAM
From the timings seen above, it should become apparent that JSON
serialization performance is, on average, noticeably slower when
compared to a simple manifest-style format. In particular, this
is very noticeable on lower memory-bandwidth SPARC systems.
It was discovered that the likely reason for poor serialization on
some SPARC systems is that simplejson uses a recursive function-
based iterative encoder that does not perform well on SPARC systems
(due to register windows?).
This is likely because the call stack depth for the encoder will
match that of any python structure that it encodes. During the
evaluation of possible format variations, this resulted in a
hybrid approach that combined a python dict with a simple list
of actions with the hope that further improvements could be made
to simplejson at some future date. Without this approach,
significant increases in write times were seen (20-30 seconds)
when using a pure dict-based structure.
Conversely though, JSON read performance, on average, is noticeably
faster compared a manifest-style format. In part, this is because
more work has to be performed to transform the manifest-style format
into an equivalent python data structure. Notably, there is a large
cost to sorting package versions after load (having the version data
in ascending order is extremely useful to the client).
Finally, a comparison of the heap size overhead (defined as the
difference between the size of the heap before loading a catalog
and after as measured on my x86 system) is shown for comparison
below:
=================================================================
'Heap' Overhead Comparison
=================================================================
Catalog Mfst. Sz. JSON Sz. Increase Sz. Inc. %
-----------------------------------------------------------------
base 9.16 MiB 12.45 MiB +3.29 MiB +35.92%
dependency 32.34 MiB 51.48 MiB +19.14 MiB +59.19%
summary 16.84 MiB 27.41 MiB +10.57 MiB +62.76%
all 39.87 MiB 63.88 MiB +24.01 MiB +60.22%
3.3 Conclusion
When comparing the numbers alone, it seems as though the manifest-
style format should have been chosen based solely on:
- lower memory usage (43.6% less than JSON on average)
- faster write times (1.71s on average compared to 4.01s on average
for JSON)
However, ultimately, the manifest-style format was rejected for
reasons beyond simple numbers:
- desire for a defined grammar and syntax
- required maintaining custom parsing and storage code
- not easily extensible such that if additional metadata
was needed that a protocol or file format revision might
be required
- when weighing read performance vs. write performance,
read performance was considered more important as updates
to the catalog will happen far less freqeuntly than loads
of package data (loads took 3.01s on average for manifest-
style compared to 0.73s on average for JSON or about 75.75%
longer)
Instead, the JSON format was selected for the following reasons:
- full unicode support
- well-defined grammar and structure
- supported data types almost exactly mirror python's own
native data types
- allowed easy storage of existing action data of which
catalogs are essentially a summarized view of
- a python library for the parsing and writing of JSON is
part of python 2.6+
- JSON is easily portable to other systems and myriad
tools are available to parse and write it
- it is anticipated that the performance of simplejson will
only improve over time
As a final note, the approach of using separate catalogs for each
set of data instead of a single, merged catalog was used to reduce
memory usage and the amount of data that needs to be transferred
for clients.
Cheers,
--
Shawn Walker
_______________________________________________
pkg-discuss mailing list
[email protected]
http://mail.opensolaris.org/mailman/listinfo/pkg-discuss