You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Set Tailwind to use dark class for its dark theme
* Slightly change light theme
* Switch to a Serif font
* Make the ToC on Articles look better, increase font size
* Merge Tips and Tutorials back together and use tags to distinguish them
(R.I.P. SEO)
* Fix badge link on Articles list
* Add symlinks for Tips and Tutorials and refactor workflow
Copy file name to clipboardExpand all lines: content/articles/20200815-rails-deprecate-activerecord-attribute.md
+8-2
Original file line number
Diff line number
Diff line change
@@ -2,25 +2,29 @@
2
2
title = "Rails: Deprecate an ActiveRecord attribute"
3
3
date = 2020-08-15
4
4
[taxonomies]
5
-
tags = ["rails"]
5
+
tags = ["tip", "rails"]
6
6
+++
7
7
8
8
## Context
9
+
9
10
When working on a growing application relying on a database, it is not uncommon to have a data model that collects dust: some of its data may become obsolete, but you may not want to delete it just yet, because you may need to migrate it, prepare its deletion later or keep it as history.
10
11
11
12
You might want to flag this data as deprecated, so that no one (including you) would rely on this no longer updated or relevant data.
12
13
13
14
## How
15
+
14
16
You could of course add comments in your code, but there would be no guarantee someone would check those comments before using those attributes. Also, you wouldn't notice anything if existing code would use those attributes indirectly.
15
17
16
18
Fortunately, Rails provides [a nice way to flag ActiveRecord attributes as obsolete][0], stabilized since Rails 4.x.
17
19
18
20
Let's create a `Deprecator` class, that'll embed any behavior we'd like to implement. We can be creative here, like:
21
+
19
22
- logging the action
20
23
- logging parts of the stacktrace
21
24
- shaming your coworkers on Slack with a bot message
22
25
23
26
But let's keep it simple for the time being:
27
+
24
28
```ruby
25
29
# app/deprecators/field_deprecator.rb
26
30
classFieldDeprecator
@@ -34,6 +38,7 @@ end
34
38
We're not respecting the semantics of the second argument, but hey, this is duck-typing :trollface:
35
39
36
40
Then, all we have to do is to add a call to this class method in our model:
41
+
37
42
```ruby
38
43
classRacoon < ApplicationRecord
39
44
deprecate \
@@ -44,11 +49,12 @@ end
44
49
```
45
50
46
51
With our current deprecator, this would output a warning message everytime this field is being read and updated:
52
+
47
53
```
48
54
> Racoon.first.owner_id
49
55
owner_id is deprecated, please favor Racoon#caretaker_id
Copy file name to clipboardExpand all lines: content/articles/20200816-developing-on-wsl.md
+11-2
Original file line number
Diff line number
Diff line change
@@ -2,22 +2,25 @@
2
2
title = "Developing on WSL"
3
3
date = 2020-08-16
4
4
[taxonomies]
5
-
tags = ["wsl", "dev"]
5
+
tags = ["tutorial", "wsl", "dev"]
6
6
+++
7
7
8
8
## Context
9
+
9
10
The latest versions of Windows 10 have seen the maturation of WSL, the Windows Subsystem for Linux. Basically, it allows installing a Linux distribution that'll use a special Linux kernel running "nativaly" on Windows 10. It doesn't run on a virtualized environment, although it seems to use a Hyper-V volume for storage.
10
11
11
12
Oh, and you can also use their shiny new [Terminal][0] for ultimate ease of use.
12
13
13
14
## The Good
14
15
15
16
### Seamless development
17
+
16
18
You will evolve in a familiar environment if you're already using VSCode, since it leverages its client/server architecture, each running on Windows and Linux respectively. Typing `code .` in your terminal will open VSCode. Extensions will be installed server-side.
17
19
18
20
Other than that, you can enjoy the benefits of using any regular Linux distribution.
19
21
20
22
### You can use Docker
23
+
21
24
Since the release of WSL 2 (included in the 2004 Windows update), you can use Docker containers inside your Linux distro, managed by your Docker daemon installed on Windows. "All" it requires is using a WSL2 compatible distribution, and enable WSL2 integration in the Docker Desktop preferences.
22
25
23
26
And, instead of using a virtual machine to host the containers, you can also set Docker Desktop to use... WSL2.
@@ -29,18 +32,21 @@ I haven't made any benchmarks, but I suppose it helps to boost performance.
29
32
I suppose you can even use your Kubernetes cluster installed with Docker on Windows from your Linux distribution, but I haven't come that far into testing yet.
30
33
31
34
### Accessing files between Linux and Windows
35
+
32
36
Your Windows volumes are automatically mounted in `/mnt`, meaning you can literally access anything from your Windows installation. Also, typing `explorer.exe .` reveals the current folder in Windows' file manager, shown as a network shared volume.
33
37
34
38
## The Bad
35
39
36
40
### Occasional bugs and freezes
41
+
37
42
I do backend development in Ruby (and Rails), Elixir (and mostly Phoenix) professionally, and I also wrote a bit of Elm and Rust in WSL2. I am quite satisfied with the quality of the integration WSL2 offers, but it isn't bug-free either.
38
43
39
-
For example, when I first tried Zola and ran its web server, I just couldn't connect to it with my browser. Firefox was just displaying "Unable to connect". The reason? I don't know, but running `wsl.exe --shutdown` would "solve" this problem. I'm obviously more annoyed by the fact that I *need* to reboot than the fact that I only lose a few seconds of my time every week or so.
44
+
For example, when I first tried Zola and ran its web server, I just couldn't connect to it with my browser. Firefox was just displaying "Unable to connect". The reason? I don't know, but running `wsl.exe --shutdown` would "solve" this problem. I'm obviously more annoyed by the fact that I _need_ to reboot than the fact that I only lose a few seconds of my time every week or so.
40
45
41
46
But the most annoying thing was occurring during the compilation of my Rust project through Docker (it never happened when I was building "on bare metal"), which was the complete freeze of VSCode, my terminal, and really, any app related to WSL. In that case, I'd need to wait for a few minutes for the build to finish and my system to be responsive again. I assume this has something to do with my building process eating all of the IO reserved for WSL, or something related to this weird Docker setup.
42
47
43
48
### Wondering about long-term stability
49
+
44
50
While Microsoft provided a migration script to switch from WSL to WSL2 (through the use of `wsl.exe`, to run in `cmd.exe` or PowerShell), it never worked on the Ubuntu I installed a few months ago, so I tried a few several other distributions (Alpine, Arch) after finally installing a fresh new Ubuntu, which worked instantly with Docker integration. I think not all distributions available on the store are compatible with WSL2, but information is a bit scarce.
45
51
46
52
So far, my setup has been very stable, but I wonder if a future Windows update could break the whole thing. I got almost anything versioned and hosted somewhere, but still.
@@ -49,7 +55,9 @@ And sure, there's bugtracking through Github, but I think WSL as a whole is pret
49
55
## The Ugly
50
56
51
57
### Get out of my $PATH
58
+
52
59
To achieve a seemless integration with Windows, my Ubuntu installation has basically the whole Windows mess injected into its default $PATH:
@@ -62,6 +70,7 @@ Whish is... yeah. It itched me in many places when I saw this, and when I saw ra
62
70
This kind of dirty black magic may annoy you if you want your distro to be 100% clean. But I guess you wouldn't want to use WSL in the first place in that was the case ;)
63
71
64
72
## Conclusion
73
+
65
74
I'm very satisfied with WSL and its related developments so far. My primary reason for using it is the need for good UI and accessibility tools, because I have an extremely low vision (but I'll run into details in a future article) but it's also really satisfying to leverage a good CPU (in that case, a Ryzen 3800X) instead of the traditional coughing overpriced Macbook Pro any startup company grants its employees with.
66
75
67
76
And of course, you can launch a game or two when you need a break.
Copy file name to clipboardExpand all lines: content/articles/20201022-elixir-clustering-on-kubernetes.md
+39-9
Original file line number
Diff line number
Diff line change
@@ -3,10 +3,11 @@ title = "Elixir: Clustering on Kubernetes"
3
3
description = "A quick walkthrough to setup a cluster with Elixir, Kubernetes and libcluster."
4
4
date = 2020-10-21
5
5
[taxonomies]
6
-
tags = ["elixir", "k8s"]
6
+
tags = ["tutorial", "elixir", "k8s"]
7
7
+++
8
8
9
9
## Context
10
+
10
11
When developing Elixir applications, you may want to create a cluster of Erlang nodes at some point, for example, to provide redundancy, high availability,or to share a global state on the cluster (with Mnesia, for example) without hitting an outside DBMS.
11
12
12
13
This can be easily achieved with `libcluster`. As you can see [in the docs][0], it supports multiple strategies.
@@ -16,17 +17,21 @@ I'll assume you want to deploy to a Kubernetes cluster, and I'll be covering thi
16
17
We'll deploy a `cluster` of Erlang `nodes`. To avoid confusion with Kubernetes terminology, I'll use the prefix `k8s` when referring to Kube.
17
18
18
19
## A word about WSL
20
+
19
21
You may run into one issue if you're using WSL: by default, WSL systems use the same hostname as their Windows host, which isn't fully qualified, and Erlang may not like that.
20
22
21
23
To fix this (and to avoid breaking WSL), you can specify a full computer name on Windows.
24
+
22
25
1. Go to your PC settings (`This PC` -> `Properties` -> `Change Settings` -> `Change`),
23
26
2. In the `Computer/Domain Changes` window, keep a simple Computer Name,
24
27
3. Click on More, and in the `DNS Suffix and NetBIOS Computer Name` window, specify a primary DNS suffix (like `localdomain`).
25
28
26
29
You can keep the other settings unchanged, provided you have something like `barney` as computer name and `barney.localdomain` as full computer name.
27
30
28
31
## Connecting nodes locally
32
+
29
33
Let's make a new clean project with Mix:
34
+
30
35
```
31
36
> mix new cluster
32
37
> cd cluster
@@ -41,57 +46,67 @@ And let's start a `iex` REPL, giving it a new argument:
41
46
Here, we just passed a flag to the Erlang VM, specifying the shortname of the node we want to run.
42
47
43
48
There's now a slight difference appearing in your prompt:
49
+
44
50
```
45
51
iex(a@{HOSTNAME})1>
46
52
```
47
53
48
54
You should see your PC hostname at the right of that `@` symbol. This is a default value, because we haven't specified a fully qualified name.
49
55
50
56
Let's start a second Elixir app, in another terminal:
57
+
51
58
```
52
59
> iex --sname b
53
60
```
54
61
55
62
And let's discover a few functions!
56
63
57
-
*`node()` (or `Node.self()`) returns the name of the current Node.
58
-
*`Node.list()` returns a list of the connected Nodes in the cluster. At this point, it should be empty.
59
-
*`Node.connect()` and `Node.disconnect()` allow you to, you guessed it, connect and disconnect nodes. Let's try it!
64
+
-`node()` (or `Node.self()`) returns the name of the current Node.
65
+
-`Node.list()` returns a list of the connected Nodes in the cluster. At this point, it should be empty.
66
+
-`Node.connect()` and `Node.disconnect()` allow you to, you guessed it, connect and disconnect nodes. Let's try it!
60
67
61
68
On the `b` Node, type:
69
+
62
70
```
63
71
Node.connect(:a@hostname)
64
72
```
65
73
66
74
Now, go back to the `a` Node, and run:
75
+
67
76
```
68
77
Node.list()
69
78
```
70
79
71
80
You should see `b@hostname` appearing there. Congratulations :) We haven't done anything spectacular this far, but this is exactly what `libcluster` will do under the hood once it's set up correctly.
72
81
73
82
## Todo
83
+
74
84
So, here's what we'll have to do:
85
+
75
86
1. Pass the relevant fully qualified name to each Erlang VM
76
87
2. Update our Kubernetes configuration
77
88
3. Define a cluster topology to configure `libcluster`
78
89
79
90
## VM Args
91
+
80
92
We'll need to pass some arguments to our Erlang VMs.
81
93
Since at least its version `1.10`, Mix can handle this (you won't need to add Distillery as a dependency).
82
94
83
95
In your project directory, run:
96
+
84
97
```
85
98
mix release.init
86
99
```
87
100
88
101
This command will generate a few files. Let's take a look at `vm.args.eex`. It should contain a few commented lines, specifically:
102
+
89
103
```sh
90
104
## Customize flags given to the VM: http://erlang.org/doc/man/erl.html
91
105
## -mode/-name/-sname/-setcookie are configured via env vars, do not set them here
92
106
```
93
107
94
108
Since we want to set `name` and deploy on Linux containers, let's get to `env.sh.eex`. There's a few commented lines in there, but we're most interested in the last block:
109
+
95
110
```sh
96
111
# Set the release to work across nodes. If using the long name format like
@@ -101,12 +116,14 @@ Since we want to set `name` and deploy on Linux containers, let's get to `env.sh
101
116
```
102
117
103
118
To enable clustering, we need to replace the `127.0.0.1` part with the fully qualified name of our pod. Kubernetes has its own internal DNS, and pods are typically named like this:
119
+
104
120
```
105
121
# Assuming our Pod IP is 192.168.0.45
106
122
192-168-0-45.namespace.pod.cluster.local
107
123
```
108
124
109
125
So your setup should end up looking like this:
126
+
110
127
```sh
111
128
export POD_A_RECORD=$(echo $POD_IP| sed 's/\./-/g')
Both `$POD_IP` and `$NAMESPACE` will have to be defined when our application starts, so we'll add those to our k8s deployment manifest.
117
134
118
135
## Kubernetes configuration
136
+
119
137
We should now declare our two new environment variables:
138
+
120
139
```yaml
121
140
apiVersion: apps/v1
122
141
kind: Deployment
@@ -155,19 +174,23 @@ spec:
155
174
```
156
175
157
176
Both variables references the pod's information once it's started. You can check it by running:e by running:
177
+
158
178
```
159
179
kubectl get pod myapp-456789 -o yaml
160
180
```
161
181
162
182
If you already have one pod correctly labelled, you can check that libcluster will correctly poll the right pods from Kube by doing what it does: polling with a labelSelector:
183
+
163
184
```
164
185
kubectl get pods -l app=myapp,tier=web
165
186
```
166
187
167
188
If this returns off-topic pods, then you should fix this before proceeding :)
168
189
169
190
## Libcluster topology
191
+
170
192
Last, but not least, we need to specify our libcluster strategy:
193
+
171
194
```elixir
172
195
topologies = [
173
196
default: [
@@ -190,29 +213,35 @@ topologies = [
190
213
Once you deploy this, you should be good :) After scaling up your deployment (`kubectl scale --replicas=X deployment/my-app`), you should see various `[libcluster]` log entries on your pods.
191
214
192
215
## What now?
216
+
193
217
There's a few things to toy around with if you want to leverage your cluster!
194
218
195
219
Starting from there, and after cleaning your configuration and/or setting up cleaner environment variables, there are a few interesting things to do. How about:
196
-
* Declaring a GenServer tracking nodes status?
197
-
* Implementing a Cluster Singleton worker using [Individual][8]?
198
-
* Toying around with Phoenix.PubSub?
199
-
* Playing with process registries, like Phoenix.Tracker?
200
-
* Discovering Erlang's [Mnesia][9], a powerful in-cluster DBMS to use for internal state or cache, and which makes Redis irrelevant?
220
+
221
+
- Declaring a GenServer tracking nodes status?
222
+
- Implementing a Cluster Singleton worker using [Individual][8]?
223
+
- Toying around with Phoenix.PubSub?
224
+
- Playing with process registries, like Phoenix.Tracker?
225
+
- Discovering Erlang's [Mnesia][9], a powerful in-cluster DBMS to use for internal state or cache, and which makes Redis irrelevant?
201
226
202
227
## Node up/down notifications
228
+
203
229
[Erlang exposes a simple function][10] that'll get the current process notified when nodes are up or down. This can allow us to react accordingly, like printing debug information in the logs, dereferencing the node, or push the self destruct button.
204
230
205
231
In our first example, if you had run the following before connecting the two nodes together:
232
+
206
233
```elixir
207
234
:net_kernel.monitor_nodes(true)
208
235
```
209
236
210
237
Then running `flush` would show you the messages you received:
238
+
211
239
```elixir
212
240
{:nodeup, :a@Hostname}
213
241
```
214
242
215
243
You can call `monitor_nodes` in a [GenServer][11], of course, and implement the relevant callbacks:
This article is a bit long and rough around the edges, but it should give you a better understanding on how to deploy Elixir applications as a cluster.
230
260
231
261
I strongly suggest that you follow the official documentations of Elixir, Erlang and libraries. The snippets I included may get outdated over time, though I don't expect the process to be easier than it currently is :)
0 commit comments