A new law unchains fusion energy

EPFL physicists revised one of the fundamental laws foundational to plasma and fusion research.

Cross-Repository operations

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

At the core of AIRport are Autonomous Interdependent Repositories. Repositories contain all of the records necessary for work on a particular unit, such as a UI page while storing everything in a relational database via a virtual Repository layer.

Record tracking

As described previously when a record gets added to a Repository it brings along with it all of the records it points to via @ManyToOne() relationships. However it still retains information about the original record that it was pointing to. This is done for every AIR Entity with the "original" fields:

@MappedSuperclass()
export class AirEntity {

    // Follwing properties contain identifiers for the
    // current repository of this (possibly copied) record
    
    @ManyToOne()
    @JoinColumn({
        name: 'REPOSITORY_ID', referencedColumnName: 'ID',
        nullable: false
    })
    repository: Repository

    @Id()
    @ManyToOne()
    @JoinColumn({
        name: 'ACTOR_ID', referencedColumnName: 'ID',
        nullable: false
    })
    actor: Actor
    
    @Id()
    @Column({ name: 'ACTOR_RECORD_ID', nullable: false })
    @GeneratedValue()
    actorRecordId: number


    // Following properties contain identifiers for the original
    // repository of this (possibly copied) record
	
    @ManyToOne()
    @JoinColumn({
        name: 'ORIGINAL_REPOSITORY_ID', referencedColumnName: 'ID',
        nullable: false
    })
    originalRepository: Repository

    @ManyToOne()
    @JoinColumn({
        name: 'ORIGINAL_ACTOR_ID', referencedColumnName: 'ID',
        nullable: false
	})
    originalActor: Actor
    
    @Column({ name: 'ORIGINAL_ACTOR_RECORD_ID', nullable: false })
    originalActorRecordId: number

}

Basic operations

The core concept of AIRport is that by default database operations do not cross Repository boundaries. This means that all relational queries will use the "repository"/"actor"/"actorRecordId" fields for joining records across database tables.

It also means that "save" method of DAO objects will modify only the Repository of the passed-in top level records. This works for the majority of use cases but sometimes you need to query across Repositories and make modifications across them.

Working across Repositories

The solution to this is to introduce the @CrossRepository() decorator to Dao methods. All database operations in Dao methods decorated with @CrossRepository() will work across repositories.

class MyDao {

    @CrossRepository()
    async myCrossRepositoryMethod(
        myRecordUuIds: string[],
        myRecords: MyRecord[]
    ) {
        // This find queries across repositories
        let myRec: QMyRecord,
            othRec: QOtherRecord
            this._find({
            select: {
                '*': Y,
                otherRecords: {}
            },
            from: [
                myRec = Q.MyRecord,
                othRec = mr.otherRecords.leftJoin()
            ],
            where: myRec.in(myRecordUuIds)
        })

        // This save works across repositories
        this.save(myRecords)
    }
}

How this works.

Invocation mechanics

The @CrossRepository() decorator is tracked on the stack through AIRport's dependency injection. At the time of object injection all cross-repository methods are marked. All database operations in these methods are marked as being cross-repository as well.

If a non cross-repository method is invoked within a cross-repository one it will still respect the repository boundaries for all of the queries executed within it. Same mechanism works for cross-repository methods being called from non cross-repository ones, in an inverse way.

Query details

Cross-repository queries are actually pretty easy. When joining records across Repositories the "original" repository/actor/actorRecordId foreign key fields are used. For records originating in this Repository "original" fields contain the same values as regular repository/actor/actorRecordId foreign key fields. So, the query works in the same way as non cross-repository equivalent, unless the records (referenced by the foreign key fields) are copied from another repository, as required in AIRport. Thus, joining by "original" fields will return the original records, not their copies within this repository (if they were copied).

Peristence details.

Persistence operations are also pretty easy. Normally AIRport will ignore nested entities that do not belong to the same repository as the parent record. In cross-repository methods AIRport will perform the save across all repositories present in the graph. Because non-"original" foreign keys of persistend records always point to the current repository, following rules apply all nested records that do not belong to the current repository:

  • Record Creation: records will be created in both the the referenced repository and in the repository of the parent record (as copies).
  • Record Updates: records will be updated in the referenced repository and a copy of the record will be created in the repository of the parent record (replacing the currently pointed to record, which will be deleted, along will all record copies it has foreign keys to).
  • Record Deletes: by definition existing nested records are in the current repository. If the deleted record's "original" foreign keys point to another repository, the pointed-to record in the other repository will be deleted as well.

Use with care

Cross-repository queries work just fine in centralized databases but are tricky in decentralized environments. This is because the referenced repository may not be present on the local device. Thus, for cross-repository queries AIRport will inspect the result set before returing it. If there are missing nested records (from repositories that are not present in the local database) it will fetch the missing repositories from the network (and subsequently re-run the query before its' result is returned). Same goes for cross-repository persistence operations - missing referenced repositories will be loaded from the network. Naturally, this can lead to poor database performance.

Enabling back-end optimizations

Eventually Cross-Repository queries will give a hint to the AIRport app servers in back-end environments. Cross-Repository operations will go against the backing relational database while normal operations will be performed in-memory against a locally cached repository, which is either already in the local in-memory database, or is pulled into memory from a wide colum database which is an order of magnitude faster than the backing relational database.

Interdependence is here

Cross-Repository operations complete the core AIRport spec by making Repositories interdependent: "AIR" in AIRport stands for "Autonomous Interdependent Repositories". When a Dao method is decorated with @CrossRepository() all of its database operations automatically load all Repositories referenced in these operations, making network requests if necessary.