Unmagnetized implosion image and magnetized image - showing that the applied magnetic field flattens the implosion shape. Credit: Bose et al.

Researches from MIT and other universities have demonstrated that strong magnetic fields flatten the shape of inertial fusion implosions.

AIR UuId

  • By Artem V. Shamsutdinov
  • June 15th, 2022

Working with raw AIRport ids is cumbersome. The reason for that is the fact that an Id of an entity is spread across 3 different entities:

MyEntity {
    actor: {
        // backed by a foreign key column in MyEntity
        id: 123
    },
    actorRecordId: 456,
    repository: {
        // backed by a foreign key column in MyEntity
        id: 789
    }
}

UUIDs

Moreover the ids are local to the AIR database and can differ between AIRport databases. Globally the records are identified by UuIds for Repository and Actor (in combination with actorRecordId). To get uuIds you have to join to the Actor and Repository tables:

MyEntity {
    actor: {
        // Exists only in 
        uuId: '123e4567-e89b-12d3-a456-426614174000'
    },
    actorRecordId: 456,
    repository: {
        uuId: 'IPFS-QmcRD4wkPPi6dig81r5sLj9Zm1gDCL4zgpEj9CfuRrGbzF'
    },
}

Performance

AIRport UUIDs are strings that are at least 90 characters long (and will probably average 92 characters in most cases):

IPFS-QmcRD4wkPPi6dig81r5sLj9Zm1gDCL4zgpEj9CfuRrGbzF-123e4567-e89b-12d3-a456-426614174000-123

Reason for pulling out UuIds out of records is space savings. There are 4 UuIds to store - Actor UuId, Repository UuId, Original Actor UuId and Original Repository UuId (with originals neccessarily populated for cross-repository queries). This adds up to 368 bytes per record. On top of that you have to add space for indexes for each one of these. With that it can come close 1K per record or more, depending on number of foreign keys and indexes.

Modern phones have plenty of storage space (latest IPhone models at the time of writing of this blog post come with at least 128GB of storage). However AIRport can also run in browser where it runs an SqlJs instance in memory. This is where the difference really matters. If web AIRport has 10 repositories loaded with just 1000 records each the UuIds alone will take up 5MB worth of RAM. As of 2022 IPhone 14s will come with 6GB or RAM (only). And, since there is no way to tell how much data users will keep in web AIRports or how long living web AIRport instances will be, every byte matters.

For this reason I'm still keeping UuIds normalized in their own tables and require joining to these tables to retrieve them. Of course joining more tables in queries has a negative impact on performace as well, but Actor and Repository are much smaller tables (especially on Mobile devices) and impact is minimal.

Usability

But the main issue with raw AIRport UuIds is usability. Not only do you have to include the related entities in queries but you also have to properly query them, which can be very error prone. On top of that, if you want to use these ids as part of a link you now have to manually compose them into a single string and decompose them at query time.

New whole "UuId"

For this reason from now on all Repository Entities now include a transient "UuId" field:

@MappedSuperclass()
export class AirEntity {

    ....

    @Transient()
    uuId: string

}

This field is implemented with a getter/setter pair. These are defined when the record is recieved from the framework (by either an AIRport App or anything that invokes APIs).

All the getter does is grab the uuids and the actorRecordId and concatenate them into one large uuId:

'IPFS-QmcRD4wkPPi6dig81r5sLj9Zm1gDCL4zgpEj9CfuRrGbzF-123e4567-e89b-12d3-a456-426614174000-123'

Automatic Retrieval

Prior to addition of uuId field querying for it required joining the Repository and Actor entities:

export class MyRecordDao {

    async findMyRecordsWithNestedRecords() {
        let m: QMyRecord
        return await this._find({
            select: {
                '*': Y,
                repository: {
                    uuId: Y
                },
                actor: {
                    uuId: Y
                }
                nested: {}
            },
            from: [
                m = Q.MyRecord,
                m.repository.innerJoin(),
                m.actor.innerJoin()
            ]
        })
    }
}

Now you can query by the "uuId" field directly, without having to explicitly specify the related select clause framents and joins. While the "uuId" field is @Transient an exception is made in its case and it can be used for querying (just like peristent fields):

export class MyDao {

    async findMyRecordsWithNestedRecords() {
        return await this._find({
            select: {
                '*': Y,
                uuId: Y,
                nested: {}
            }
        })
    }
}

Note that it is not included with all of the persisted records in '*' and must be specified explicitly in the query to be retrieved.

Easier Queries

To make querying "by uuId" easier Dao also includes the "findByUuId" method which takes the composite UuId, and automatically constructs the necessary join to query the record.

For easy querying in joins all Query entites now include an "equals" and "in" methods that allow for joining by the composite UuId (or just joining to another entity by a Query Object or actual object references).

export class MyDao {

    async myFind(
        compositeBUuId: string,
        compositeCUuIds: string[],
    ): PromiseMyRecord[]> {
        let m: QMyRecord,
            b: QMyBRecord,
            c: QMyCRecord
        return await this._find({
            select: {
                '*': Y,
                uuId: Y,
                b: {
                    '*': Y,
                    'c': {}
                }
            },
            from: [
                m = Q.MyRecord,
                b = m.b.leftJoin(),
                c = b.c.leftJoin()
            ],
            where: and(
                // equals on the query entity object
                b.equals(compositeBUuId),
                // in on the query entity object
                c.in(compositeCUuIds)
            )
        })
    }
}

Final effect

Putting in this tooling makes AIRport UUID scheme much more developer friendly. The Application developer now work with the 'uuId' field directly and don't have interface with the underlying support table structure.