155 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
| ---
 | |
| stage: Data Stores
 | |
| group: Database
 | |
| info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
 | |
| ---
 | |
| 
 | |
| # Creating enums
 | |
| 
 | |
| When creating a new enum, it should use the database type `SMALLINT`.
 | |
| The `SMALLINT` type size is 2 bytes, which is sufficient for an enum.
 | |
| This would help to save space in the database.
 | |
| 
 | |
| To use this type, add `limit: 2` to the migration that creates the column.
 | |
| 
 | |
| Example:
 | |
| 
 | |
| ```ruby
 | |
| def change
 | |
|   add_column :ci_job_artifacts, :file_format, :integer, limit: 2
 | |
| end
 | |
| ```
 | |
| 
 | |
| ## All of the key/value pairs should be defined in FOSS
 | |
| 
 | |
| **Summary:** All enums needs to be defined in FOSS, if a model is also part of the FOSS.
 | |
| 
 | |
| ```ruby
 | |
| class Model < ApplicationRecord
 | |
|   enum platform: {
 | |
|     aws: 0,
 | |
|     gcp: 1      # EE-only
 | |
|   }
 | |
| end
 | |
| ```
 | |
| 
 | |
| When you add a new key/value pair to a `enum` and if it's EE-specific, you might be
 | |
| tempted to organize the `enum` as the following:
 | |
| 
 | |
| ```ruby
 | |
| # Define `failure_reason` enum in `Pipeline` model:
 | |
| class Pipeline < ApplicationRecord
 | |
|   enum failure_reason: Enums::Pipeline.failure_reasons
 | |
| end
 | |
| ```
 | |
| 
 | |
| ```ruby
 | |
| # Define key/value pairs that used in FOSS and EE:
 | |
| module Enums
 | |
|   module Pipeline
 | |
|     def self.failure_reasons
 | |
|       { unknown_failure: 0, config_error: 1 }
 | |
|     end
 | |
|   end
 | |
| end
 | |
| 
 | |
| Enums::Pipeline.prepend_mod_with('Enums::Pipeline')
 | |
| ```
 | |
| 
 | |
| ```ruby
 | |
| # Define key/value pairs that used in EE only:
 | |
| module EE
 | |
|   module Enums
 | |
|     module Pipeline
 | |
|       override :failure_reasons
 | |
|       def failure_reasons
 | |
|         super.merge(job_activity_limit_exceeded: 2)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 | |
| ```
 | |
| 
 | |
| This works as-is, however, it has a couple of downside that:
 | |
| 
 | |
| - Someone could define a key/value pair in EE that is **conflicted** with a value defined in FOSS.
 | |
|   For example, define `job_activity_limit_exceeded: 1` in `EE::Enums::Pipeline`.
 | |
| - When it happens, the feature works totally different.
 | |
|   For example, we cannot figure out `failure_reason` is either `config_error` or `job_activity_limit_exceeded`.
 | |
| - When it happens, we have to ship a database migration to fix the data integrity,
 | |
|   which might be impossible if you cannot recover the original value.
 | |
| 
 | |
| Also, you might observe a workaround for this concern by setting an offset in the `EE` module's values.
 | |
| For example, this example sets `1000` as the offset:
 | |
| 
 | |
| ```ruby
 | |
| module EE
 | |
|   module Enums
 | |
|     module Pipeline
 | |
|       override :failure_reasons
 | |
|       def failure_reasons
 | |
|         super.merge(job_activity_limit_exceeded: 1_000, size_limit_exceeded: 1_001)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 | |
| ```
 | |
| 
 | |
| This looks working as a workaround, however, this approach has some downsides that:
 | |
| 
 | |
| - Features could move from EE to FOSS or vice versa. Therefore, the offset might be mixed between FOSS and EE in the future.
 | |
|   For example, when you move `job_activity_limit_exceeded` to FOSS, you see `{ unknown_failure: 0, config_error: 1, job_activity_limit_exceeded: 1_000 }`.
 | |
| - The integer column for the `enum` is likely created [as `SMALLINT`](#creating-enums).
 | |
|   Therefore, you need to be careful of that the offset doesn't exceed the maximum value of 2 bytes integer.
 | |
| 
 | |
| As a conclusion, you should define all of the key/value pairs in FOSS.
 | |
| For example, you can write the following code in the above case:
 | |
| 
 | |
| ```ruby
 | |
| class Pipeline < ApplicationRecord
 | |
|   enum failure_reason: {
 | |
|     unknown_failure: 0,
 | |
|     config_error: 1,
 | |
|     job_activity_limit_exceeded: 2
 | |
|   }
 | |
| end
 | |
| ```
 | |
| 
 | |
| ## Add new values in the gap
 | |
| 
 | |
| After merging some EE and FOSS enums, there might be a gap between the two groups of values:
 | |
| 
 | |
| ```ruby
 | |
| module Enums
 | |
|   module Ci
 | |
|     module CommitStatus
 | |
|       def self.failure_reasons
 | |
|         {
 | |
|           # ...
 | |
|           data_integrity_failure: 12,
 | |
|           forward_deployment_failure: 13,
 | |
|           insufficient_bridge_permissions: 1_001,
 | |
|           downstream_bridge_project_not_found: 1_002,
 | |
|           # ...
 | |
|         }
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 | |
| ```
 | |
| 
 | |
| To add new values, you should fill the gap first.
 | |
| In the example above add `14` instead of `1_003`:
 | |
| 
 | |
| ```ruby
 | |
| {
 | |
|   # ...
 | |
|   data_integrity_failure: 12,
 | |
|   forward_deployment_failure: 13,
 | |
|   a_new_value: 14,
 | |
|   insufficient_bridge_permissions: 1_001,
 | |
|   downstream_bridge_project_not_found: 1_002,
 | |
|   # ...
 | |
| }
 | |
| ```
 |