Quick review of the most popular ways to implement flags


Introduction #

There are many ways to store flags and use them for communication between client <-> backend service or service <-> service. In the article, we will review the most popular options and try to help to choose the correct way for the next project.

The most popular ways to work with flags are:

  1. Store in separate column in DB or separate property in a class/struct
  2. Store as an array or in a set
  3. Store as binary data in a variable

Options #

Separate field #

It means we will create a separate column in DB for each flag.

DB Example #

In an example, is_new will be a flag.

idusernameis_new
1test1true
2test2false

JSON Example #

In a code or a NoSQL database representation will be:

[
	{
		"id": 1,
		"username": "test1",
		"is_new": true
	},
	{
		"id": 2,
		"username": "test2",
		"is_new": false
	}
]

Pros #

  • Simple to read
  • Simple to understand
  • Self-documented
  • In a code, it can represent as typed properties
  • Simple to convert between different representations, like struct/class -> JSON -> protobuf -> MsgPack -> DB row -> struct/class
  • Simple to use in search and aggregation requests in DB (for example by SQL)
  • Can be updated in DB in parallel, by UPDATE query

Cons #

  • Takes up memory/storage space/traffic
  • Could be expensive in development or risky to remove in case of dynamically typed languages
  • Could be expensive in development to add or remove or update in case of microservice architecture
  • Could be expensive in development to remove from DB as a column

Use Cases #

In my opinion, before using this approach, we should answer “yes” for all listed options in the checklist:

  • I agree to add and remove columns in your database (or add fields to the NoSQL database)?
  • I understand it could take up resources in DB will be used
  • I do not have memory and traffic sensitive clients or services

Store In Array #

In the current implementation, we will store all flags in plain names.

DB Example #

In a DB, we are going to store values in a separate table as one to many relationships.

Table user:

idusername
1test1
2test2

Table user_flag

iduser_idflag
11is_new
21is_test

Every user_flag row is linked to a row in user table by user_id field. It can be optimized to not store the flag as a string, but it is not so important in our current topic.

JSON Example #

In a code or a NoSQL database representation will be:


[
	{
		"id": 1,
		"username": "test1",
		"flags": [
			"is_new",
			"is_test"
		]
	},
	{
		"id": 2,
		"username": "test2",
		"flags": []
	}
]

Pros #

  • Simple to read
  • Simple to understand
  • Self-documented
  • Could be dynamically extended in a code (does not need to update code to add new flag)
  • Simple to convert between different representations, like struct/class -> JSON -> protobuf -> MsgPack -> DB row -> struct/class
  • Pretty simple to use in search and aggregation requests in DB (for example by SQL)
  • Can be updated in DB in parallel, by INSERT query and DELETE query

Cons #

  • Takes up memory/storage space/traffic
  • Need to write additional code for set and unset and find functions
  • Could affect different limitation in case of many flags
  • Could affect performance, especially on a client-side

User Cases #

In my opinion, before using this approach, we should answer “yes” for all listed options in the checklist:

  • I do not have performance and memory and traffic sensitive clients or services
  • I understand it could take up resources on all sides it would be used

Store In Bitmask #

Bitmask is a way when we use bitwise operations to get access to specific flag. Every flag stores in binary data in memory, for example it can be stored in: byte, short, int, uint, long, []byte, []short, map[int]byte, etc.

Simply read README.md from binflags library.

DB Example #

In DB, we can store bitmask in various types, like: TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT, BLOB types.

idusernameflags
1test11
2test20

In the current example, flags field has the type INT, and the first bit is is_new flag.

JSON Example #

In a code or a NoSQL database representation will be:


[
	{
		"id": 1,
		"username": "test1",
		"flags": 1
	},
	{
		"id": 2,
		"username": "test2",
		"flags": 0
	}
]

Pros #

  • Simple to understand
  • The fastest implementation for all operations with flags
  • Do not take up additional memory and traffic resources (always only 1 bit per flag)
  • Can be used in a search by DB (but cannot be efficiently used indexes)

Cons #

  • In some cases, flags cannot be updated in parallel
  • Cannot search flags by index
  • Have to be documented (name to bit mapping)
  • Should be explained for some people

Use Cases #

In my opinion, before using this approach, we should answer “yes” for all listed options in the checklist:

  • I want to save memory/traffic/processor resources
  • I understand all limitations

Conclusion #

As we can see, as usual, we do not have a silver bullet for all use cases and systems. But the provided list of implementations can help find the best solution for a specific project.

If you want to use the most efficient option in Go, I would suggest checking Go implementation of binary flags for various types.