summaryrefslogtreecommitdiff
path: root/doc/development/creating_enums.md
blob: d0f04c30c7be48c5213d029da9dd16d42e8df3dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
---
stage: Enablement
group: Database
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---

# 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_if_ee('EE::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(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.
  e.g. Define `activity_limit_exceeded: 1` in `EE::Enums::Pipeline`.
- When it happens, the feature works totally different.
  e.g. We cannot figure out `failure_reason` is either `config_error` or `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 EE'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(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.
  e.g. When you move `activity_limit_exceeded` to FOSS, you'll see `{ unknown_failure: 0, config_error: 1, 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 simply write the following code in the above case:

```ruby
class Pipeline < ApplicationRecord
  enum failure_reason: {
    unknown_failure: 0,
    config_error: 1,
    activity_limit_exceeded: 2
  }
end
```