Go-Room: Internals

Aditya Patwardhan
AdOnMo Tech
Published in
4 min readJun 24, 2020

--

Delving into details of how go-room works

If you haven’t read my article on a primer for Go-Room in practice, I strongly recommend reading it as a prerequisite. In the primer, I discussed the primary intention of Go-Room which is to serve as a version management tool for edge data stores.

The Go-Room Charter

The charter for Go-Room is simple and short:

  1. Keep database in sync with the expectations of your application code. Plainly, make sure that all tables expected by the app are present and have the latest expected schema.
  2. In the event of a mismatch in terms of schema expectations migrate the data store using user provided migration scripts.
  3. Signal error if the data store cannot be brought up to expected state thus enabling the app to make an informed decision about the data store usage.

Charter Article 1: Keep database in sync with application code

To enforce this article Go-Room needs to have the ability to generate a stable signature of schema expected by the application code. Application code represents the schema expectations in the form of model structs that the ORM understands.

Go-Room supports GORM integration out-of-the-box. GORM takes the struct object and creates its own definition of schema by looking at the Tags associated with the struct fields.

In the snippet above you can see how User is represented as a Foreign Key relationship to Profile.

So in order to generate a stable signature of the schema all we need is a data structure that represents how the ORM views the schema. Refer to the gorm-adapter in Go-Room code-base for a reference implementation for GORM models. Once we have a list of data structures each representing a table in our current schema, we feed this to an algorithm that can produce a stable hash.

For reference I used an implementation inspired by recursive-deep-hash. In previous article IdentityHashCalculator was one of the boilerplate components we had to create to serve this function.

For an overall understanding of how a stable signature is generated refer to entity.go in the code-base. At a high level, we sort model representation by the table names and then calculate a deep hash of the array to generate the signature.

Flow of signature generation process

Go-Room creates a metadata table in the database to track the version-number and associated signature of the schema currently active. Whenever an app calls Room.Init the version number and signature computed based on the arguments presented are matched against the stored values. This serves as a simple way to verify if the database is in sync with the application code.

Charter Article 2: Migrate the database schema to expected version

To enforce this article Go-Room expects a standard interface to the ORM and Migration scripts of the underlying database.

Article 2 is called into effect when the database is not in sync with the application code’s expectations.

Scenario 1

The stored version number is same as expected but schema signatures are different. This is a catastrophic scenario. Room.Init is simply expected to fail

Scenario 2

The stored version number is unequal to the expected version. In this case Go-Room sifts through all the migration scripts provided as arguments and generates a Graph. It then applies a DFS to find the shortest path to migrating the database to the expected version. If no migration path is found Init fails.

If a migration path is found then Go-Room executes the migration scripts and update on metadata table in a single transaction. This is to prevent half baked changes to the database in event of migration script failure.

Charter Article 3: Signal error if database not usable

Go-Room performs database sanity check and applies required migrations on each Init call. In event of a failure in any of these steps it signals an appropriate error to the user.

Go-Room is a utility for developers to ease out version management. It affords the developer all the freedom when it comes to handling Init failures. This is to ensure that appropriate actions can be taken as deemed by the application developer. For instance they may choose to send a copy of the database files to the server for backup before purging it locally in event of an Init error.

What Next?

I hope I was able to give a deeper insight into the functioning of go-room in this article. I would strongly recommend looking at the ORM and IdentityHashCalculator interfaces and their reference implementations in the code-base.

You might have a question as to “There is a lot said about GORM. How do we use Go-Room with a different ORM or data store?”. I intend to address this in my next article “Go-Room: Advanced Usage”.

Thanks for reading :). I look forward to your questions and feedback.

--

--