Skip to content

Commit ca15ef6

Browse files
authored
Fetch more jobs messages, add diagnostics for job board startup (#435)
* Lots more details around ops and debugging production * Extend message fetch to 90 days * Don't limit how many messages get fetched * Debug logging, why aren't we getting as many HIRING posts as expected Also it appears the FORHIRE posts aren't getting cleaned up as intended
1 parent ace4a02 commit ca15ef6

File tree

2 files changed

+91
-30
lines changed

2 files changed

+91
-30
lines changed

README.md

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ See [the contributing guide](./CONTRIBUTING.md) for specific instructions.
1212

1313
You must have a working `.env` file that enables you to run the bot locally. With the values in `.env`, create a `reactibot-env` secret in your local minikube cluster. Everything must be provided a value, but it's okay if some of them are just random strings.
1414

15-
```sh
16-
kubectl create secret generic reactibot-env \
15+
```bash
16+
$ kubectl create secret generic reactibot-env \
1717
--from-literal=DISCORD_HASH= \
1818
--from-literal=DISCORD_PUBLIC_KEY= \
1919
--from-literal=DISCORD_APP_ID= \
@@ -31,29 +31,29 @@ Set up kubectl and minikube locally. It's kinda hard.
3131

3232
Start up a local Docker image registry.
3333

34-
```sh
35-
docker run -d -p 5000:5000 --name registry registry:2.7
34+
```bash
35+
$ docker run -d -p 5000:5000 --name registry registry:2.7
3636
```
3737

3838
`-d` means this will run in "detached mode", so it will exit without logs after pulling required images and starting. You can view logs for it with `docker logs -f registry`.
3939

4040
Create a file, `k8s-context`, in the project root, alongside the Dockerfile, with an IMAGE variable for kubectl to use.
4141

42-
```sh
43-
echo IMAGE=reactibot:latest > k8s-context
42+
```bash
43+
$ echo IMAGE=reactibot:latest > k8s-context
4444
```
4545

4646
Run a docker build and tag it. We need to retrieve the image ID of the build we run, which complicates the command.
4747

48-
```sh
49-
docker build . -t reactibot
50-
docker tag $(docker images reactibot:latest | tr -s ' ' | cut -f3 -d' ' | tail -n 1) localhost:5000/reactibot
48+
```bash
49+
$ docker build . -t reactibot
50+
$ docker tag $(docker images reactibot:latest | tr -s ' ' | cut -f3 -d' ' | tail -n 1) localhost:5000/reactibot
5151
```
5252

5353
Run a local deploy.
5454

55-
```sh
56-
kubectl apply -k .
55+
```bash
56+
$ kubectl apply -k .
5757
```
5858

5959
If it doesn't deploy correctly (e.g. `kubectl get pods` shows a status other than success), you can debug it with `kubectl describe pod reactibot-deployment`
@@ -64,28 +64,79 @@ I actually couldn't get a local registry working so I fell back on using ghcr.io
6464

6565
[Create a Personal Access Token (Classic)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) and log in to ghcr.io. Use the PAT(C) as your password.
6666

67-
```sh
68-
docker login ghcr.io
67+
```bash
68+
$ docker login ghcr.io
6969
```
7070

7171
Create a file, `k8s-context`, in the project root, alongside the Dockerfile, with an IMAGE variable for kubectl to use.
7272

73-
```sh
74-
echo IMAGE=ghcr.io/<your gh>/reactibot:test > k8s-context
73+
```bash
74+
$ echo IMAGE=ghcr.io/<your gh>/reactibot:test > k8s-context
7575
```
7676

7777
Run a docker build, tag it, and push to the registry. We need to retrieve the image ID of the build we run, which complicates the command.
7878

79-
```sh
80-
docker build . -t <your gh>/reactibot:test
81-
docker tag $(docker images <your gh>/reactibot:test | tr -s ' ' | cut -f3 -d' ' | tail -n 1) ghcr.io/<your gh>/reactibot:test
82-
docker push ghcr.io/<your gh>/reactibot:test
79+
```bash
80+
$ docker build . -t <your gh>/reactibot:test
81+
$ docker tag $(docker images <your gh>/reactibot:test | tr -s ' ' | cut -f3 -d' ' | tail -n 1) ghcr.io/<your gh>/reactibot:test
82+
$ docker push ghcr.io/<your gh>/reactibot:test
8383
```
8484

8585
Run a local deploy.
8686

87-
```sh
88-
kubectl apply -k .
87+
```bash
88+
$ kubectl apply -k .
8989
```
9090

9191
If it doesn't deploy correctly (e.g. `kubectl get pods` shows a status other than success), you can debug it with `kubectl describe pod reactibot-deployment`
92+
93+
# Inspecting the production operations
94+
95+
```bash
96+
$ kubectl get ingress
97+
NAME CLASS HOSTS ADDRESS PORTS AGE
98+
mod-bot-ingress nginx euno.reactiflux.com ….….….… 80, 443 …
99+
reactibot-ingress nginx api.reactiflux.com ….….….… 80, 443 …
100+
101+
$ kubectl get svc
102+
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
103+
kubernetes ClusterIP 10.….….… <none> 443/TCP …
104+
mod-bot-service ClusterIP 10.….….… <none> 80/TCP …
105+
reactibot-service ClusterIP 10.….….… <none> 80/TCP …
106+
107+
$ kubectl get pods --all-namespaces
108+
NAMESPACE NAME READY STATUS RESTARTS AGE
109+
cert-manager cert-manager-…-…-… 1/1 Running 0 …
110+
cert-manager cert-manager-…-… 1/1 Running 0 …
111+
cert-manager cert-manager-webhook-…-… 1/1 Running 0 …
112+
default mod-bot-set-0 1/1 Running 0 …
113+
default reactibot-deployment-…-… 1/1 Running 0 …
114+
ingress-nginx ingress-nginx-controller-…-… 1/1 Running 0 …
115+
```
116+
117+
## Useful diagnostic references
118+
119+
```bash
120+
# Create a debug pod
121+
$ kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot -- /bin/bash
122+
123+
# Then from inside the pod:
124+
curl http://mod-bot-service
125+
curl http://reactibot-service
126+
127+
# Check service endpoints
128+
# This should show the IP addresses of your pods. If empty, the service isn't finding the pods.
129+
$ kubectl get endpoints mod-bot-service reactibot-service
130+
NAME ENDPOINTS AGE
131+
mod-bot-service 10.244.0.231:3000 58d
132+
reactibot-service 10.244.0.244:3000 6d4h
133+
134+
# View the logs for the ingress controller
135+
$ kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=100
136+
137+
# Check if the pods are actually listening on port 3000
138+
# mod-bot, a Stateful Set
139+
$ kubectl exec -it mod-bot-set-0 -- netstat -tulpn | grep 3000
140+
# reactibot, a Deployment
141+
$ kubectl exec -it $(kubectl get pod -l app=reactibot -o jsonpath='{.items[0].metadata.name}') -- netstat -tulpn | grep 3000
142+
```

src/features/jobs-moderation/job-mod-helpers.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,25 +85,35 @@ export const getJobPosts = () => {
8585
};
8686
};
8787

88-
const DAYS_OF_POSTS = 30;
88+
const DAYS_OF_POSTS = 90;
8989

9090
export const loadJobs = async (bot: Client, channel: TextChannel) => {
9191
const now = new Date();
9292

9393
let oldestMessage: StoredMessage | undefined;
9494

9595
// Iteratively add all messages that are less than DAYS_OF_POSTS days old.
96-
// Fetch by 10 messages at a time, paging through the channel history.
9796
while (
9897
!oldestMessage ||
9998
differenceInDays(now, oldestMessage.createdAt) < DAYS_OF_POSTS
10099
) {
101-
const newMessages: StoredMessage[] = (
102-
await channel.messages.fetch({
103-
limit: 10,
104-
...(oldestMessage ? { after: oldestMessage.message.id } : {}),
105-
})
106-
)
100+
const messages = await channel.messages.fetch({
101+
...(oldestMessage ? { after: oldestMessage.message.id } : {}),
102+
});
103+
console.log(
104+
"[DEBUG] loadJobs()",
105+
`Oldest message: ${oldestMessage ? oldestMessage.createdAt : "none"}.`,
106+
"Just fetched",
107+
messages.size,
108+
"messages",
109+
messages
110+
.map(
111+
(m) =>
112+
`${m.author.username}, ${differenceInDays(now, m.createdAt)} days old`,
113+
)
114+
.join("; "),
115+
);
116+
const newMessages: StoredMessage[] = messages
107117
// Convert fetched messages to be stored in the cache
108118
.map((message) => {
109119
const { tags, description } = parseContent(message.content)[0];
@@ -151,7 +161,7 @@ export const deleteAgedPosts = async () => {
151161
console.log(
152162
`[INFO] deleteAgedPosts() ${
153163
jobBoardMessageCache.forHire.length
154-
} forhire posts. max age is ${FORHIRE_AGE_LIMIT} JSON: \`${JSON.stringify(
164+
} forhire posts. max age is ${FORHIRE_AGE_LIMIT} hours JSON: \`${JSON.stringify(
155165
jobBoardMessageCache.forHire.map(({ message, ...p }) => ({
156166
...p,
157167
hoursOld: differenceInHours(new Date(), p.createdAt),

0 commit comments

Comments
 (0)