This commit is contained in:
zleyyij 2023-06-09 19:52:34 -06:00
parent 7cd795c2f7
commit 495623eacf
5 changed files with 396 additions and 0 deletions

View File

@ -0,0 +1,240 @@
The purpose of this post is to explain async javascript in a practical manner, and how to apply it.
# Concurrent programming?
Asynchronous (or concurrent programming) is a form of programming that allows the code to address multiple tasks at the same time. This can practically be applied while you're waiting for an operation to complete, and want to do other things until the operation is complete. One way to visualize this is with a teapot, and a few cups of tea, as well as people drinking the tea. You can fill up someone else's tea while you wait for someone to finish their tea. You're constantly moving the teapot around, filling different cups. That example is specifically for single threaded concurrency, while multithreading could be thought of as having multiple teapots that you use to fill up multiple teacups at the same time. With one teapot, you can only fill up one cup of tea at a time. Following this metaphor, synchronous programming would be filling up a cup of tea, and then moving onto the next person, and waiting for them to finish tea. You wait for them to finish their tea, then fill it up. You might be leaving cups of tea empty for a long time, because you're sitting, waiting for someone to finish their tea. There's a few flaws in this example, but it's good enough to explain core concepts.
Efficient asynchronous code will spend as little time as possible waiting while there's work to be done. You probably only have one teapot, use it wisely.
# The Javascript event loop
By default, Javascript is executed top to bottom, in order.
Example:
```js
// the next line will not start execution until the previous statement completes.
console.log("called first, executing first");
console.log("called second, executing second");
// the same happens for blocks. Blocks may be called multiple times (for, while), but the
// code inside of a block is still executed from top to bottom, and the code after a block
// will not be executed until each statement in a block has be run
// this code will be run 3 times
for (let i = 1; i < 4; i ++) {
console.log(`Call #${i}`);
}
console.log("called last, executing last");
```
Expected output:
```
called first, executing first
called second, executing second
1
2
3
called last, executing last
```
Concurrent JS allows your code to execute out of order.
# Event handlers
Event handling is a very basic form of concurrent Javascript. You define code that you want to run when something happens. Event driven programming is strictly well, event driven. You need to have a reliable way to determine when an event happens, and a way to call that code when the event happens. Callbacks should be implemented carefully, because badly implemented callbacks can be extremely difficult to debug.
Example:
```js
// Callbacks are probably the most common way to implement event driven programming.
// A callback is just a function passed as an argument to another function, so that
// that function can call it at a predefined time.
// "onclick" determines when you want the callback to be called
document.getElementById("thatButton").addEventListener("onclick", () => {
console.log("Someone pressed a button!");
});
```
# Asynchronous concepts in Javascript
Concurrent Javascript has 3 primary keywords: `Promise`, `await`, and `async`.
## `Promise`
A `Promise` is an object that represents the current state of an operation.
A promise will always be in one of three states:
- `pending`: The operation is still not complete. There's still work to be done, check back later.
- `fufilled`: The operation has completed successfully, and so I'm done working. If I have data for you, you can now access it.
- `rejected`: The operation has failed. I don't have data for you, and was unable to do what I was trying to do.
You need to be careful when reading from a `Promise`. If you try to use the data contained in a promise before it's resolved, it can cause unexpected behavior. Race conditions can severely impact your program.
### Interacting with Promises
There's a few important ways to interact with a promise:
- `.then()`:
`.then()` can be called on a promise, and you can use it to define what you want to happen after when a promise is no longer `pending`. You can pass a callback to `.then()`, and it's executed.
```js
// this code is going to wait for 300ms, then resolve
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Called first, executing second");
}, 300);
});
/*
The callback passed to `.then()` takes two possible arguments. `resolve`, and `reject`
`resolve` is passed the value contained within the `promise`, and `reject` is passed
any errors that occur during operation.
*/
myPromise.then((data, error) => {
// In this case, we know the `Promise` cannot fail, but if you wanted to address
// an error, you could do it here.
if (error) {
throw error;
}
console.log(data);
});
// while the promise is pending, the event loop continues
// once the promise is completed, the callback is executed
console.log("called second, executing first");
```
Expected output:
```
called second, executing first
called first, executing second
```
- `.catch()`
`.catch()` allows you to define a function to be called when a promise is rejected. This enables graceful error handling, and is somewhat analogous to a `try`/`catch` chain for a `Promise`.
```js
const thisPromiseWillFail = new Promise((resolve, reject) => {
throw new Error("This didn't work");
});
// now we can gracefully catch this error
thisPromiseWillFail.catch((error) => {
console.error(error);
});
console.log("Look mom, I'm still running!");
```
- `.finally()`
`.finally()` can be used to make interactions with chained promises more elegant. The callback accepts no arguments, and `.finally()` will return the promise it was called on without altering it. The callback passed will be called when the promise is completed, regardless of success or failure.
```js
// It's a lot less common to define promises like this, you'll probably use
// `async` functions.
const coinFlip = new Promise((resolve, reject) => {
// randomly resolve or reject a promise
if (Math.random() > 0.5) {
resolve("Heads!");
} else {
reject("Tails!");
}
});
coinFlip.finally(() => {
// This code is going to be called whether or not the promise was resolved
// or rejected, and `.finally()` will return whatever it was passed
console.log("Coin finished flipping");
})
.then((data, error) => {
// we're going to handle this error with a `.catch()` call, but we could do it here
if (error) {
return;
}
// just proving that heads was actually flipped
console.assert((data === "Heads!");
console.log("Promise resolved, you flipped heads");
})
.catch((error) => {
// When trying to fufil your promise, an error occured
console.log("You flipped tails");
});
```
## `async`
If a function is defined with `async` before the function declaration, it allows the use of `await` within the function body.
```js
// here's one way to define an async function
async function() {
}
// you can make callbacks async
const thatFunc = async () => {};
```
Async functions will always return a `Promise`, whether implicitly, or explicitly. This enables you to make use of the methods mentioned above (`then`, `catch`, `finally`).
```js
// This function returns a promise with "hello" in it
async function hiThere() {
return "hello";
}
// This function also returns a fufilled promise with "hello" in it
async function hello() {
return Promise.resolve("hello")
}
// You can even return a promise without an async function
function hey() {
return Promise.resolve("hello");
}
```
There's some minor differences in what they return with references and whatnot, they usually don't matter, but you can read up [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) if you're curious.
## `await`
`await` is the middleware between asynchronous promise wrapped values, and synchronous world of javascript. It can be used on the top level, or in `async` function declarations. When put before a function or value, `await` will *pause execution* of the surrounding `async` function until a `Promise` is either `fufilled`, or `rejected`. If the promise is rejected, than an error is thrown, and the function containing the `await` expression will appear in the stack trace. If a promise is fulfilled, than execution continues, and that line is evaluated to the value inside of the promise. If `await` is put before a value that's *not* a promise, it wraps the value in a `Promise` and evaluates it.
```js
// this function is going to return a promise that's `pending` for two seconds,
// then resolves.
function resolveAfter2Seconds() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(null);
});
});
}
async function myAsyncFunction() {
// this is evaluated, then we move on, like a normal function
console.log("called first, executing first");
// This function returns a `Promise`. It's pending,
// so execution of this function is paused until it's resolved
// because await was used.
// in the mean time, the code after the function call is evaluated.
await resolveAfter2Seconds();
// two seconds later, the promise has been resolved, and execution continues
console.log("called second, executing third (after await)");
}
// because we aren't awaiting `myAsyncFunction` right here, the event loop will continue
// past this because it's pending.
// If we wanted to use the return value of `myAsyncFunction`, we could put an `await`
// before the call.
myAsyncFunction();
console.log("called third, executing second");
```
Output:
```
called first, executing first
called third, executing second
called last, executing last
```
### What happens while we're waiting?
Rather than block execution of the thread, we can do other things (pour someone else's tea if you will) while we wait for the promise to be completed. While we're `await`ing a promise, we can continue evaluating stuff after the `async` function call.
Once we've run out of code to evaluate, and hit the end of our [tick](https://nodejs.dev/en/learn/understanding-processnexttick/), or one full cycle of the event loop, we go back to the top of the event loop, checking to see if any promises have been completed. If a promise has been completed, the async function resumes execution.

113
Kubernetes.md Normal file
View File

@ -0,0 +1,113 @@
# Vocabulary
https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-intro/ - Good summary of the different parts
## Cluster
The largest unit, a kubernetes cluster is a collection of one or more worker machines (virtual or physical machines), and the control plane.
## Control plane
A control plane is in charge of managing worker nodes and pods in a cluster.
## Node
[A node](https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-intro/) is a virtual or physical machine that in Kubernetes, and is managed by the control plane. A Node can have multiple pods, and the control plane is in charge of scheduling pods across nodes.
Each node runs at least:
- A Kubelet, the process responsible for handling communication between the control plane and the node. It manages the pods and containers running on a machine
- A [container runtime](https://kubernetes.io/docs/setup/production-environment/container-runtimes/) responsible for pulling the container image from a registry, unpacking the container, and running the application. Examples include containerd and docker.
## Deployment
A [deployment](https://kubernetes.io/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/) instructs kubernetes how to create and update instances of an application. It can be imagined similar to a blueprint. The term 'deployment' may also refer to the group of pods and replicas defined by one or more configuration files.
## Pod
A [pod](https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-intro/) is an abstraction that represents a group of one or more application containers, and shared resources. Some shared include storage and a private network. They are meant to represent a logical host, and a rough parallel might be a physical machine running a service, although this machine may have multiple network adapters. A deployment creates pods, and not containers.
## ReplicaSet
A [ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/), often referred to as a pod replica, is an identical copy of pod. They can be used for many things, most commonly high availability and load balancing.
## Service
A [service](https://kubernetes.io/docs/concepts/services-networking/service/) in Kubernetes is is an abstraction that defines a logical set of Pods and a policy to access them. A Service is defined using YAML or JSON, and is primarily used to expose a network application that's running on one or more Pods in a cluster. They also route traffic across a set of Pods, allowing Pods to die and replicate without impacting the application.
## `kubectl`
`kubectl` is the Kubernetes command line tool used to run commands against clusters. You can use `kubectl` to deploy applications, inspect and manage cluster resources, and view logs. The general format of the command follows `kubectl [action] [resource]`
## `minikube`
`minikube` creates a local Kubernetes cluster for development. It can be configured to deploy on bare metal, containers (like docker), or a VM (QEMU).
# Commands
## Common commands
- **kubectl get** - list resources
- **kubectl describe** - show detailed information about a resource
- **kubectl logs** - print the logs from a container in a pod
- **kubectl exec** - execute a command on a container in a pod
## Start `minikube`
```
minikube start
```
## Stop `minikube`
```
minikube stop
```
## Expose services running in minikube
```
minikube services
```
## View the dashboard for `minikube`(run in new terminal window)
```
minikube dashboard
```
## Check that `kubectl` is configured to talk to the cluster
```
kubectl version
```
## View nodes in the server
```
kubectl get nodes
```
## List deployments
```
kubectl get deployments
```
## Start a proxy to access the private network
```
kubectl proxy
```
## Access the API of a pod (through the proxy)
```
curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/
```
Where `$POD_NAME` is the name of a pod, you can get this information with `kubectl get pods`
## Start a Bash session in a Pod's container
```
kubectl exec --ti $POD_NAME -- bash
```
## Create a new Service and expose it to external traffic
```
kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
```
## Apply a new label to pods
```
kubectl label pods "$POD_NAME" version=v1
```
## See if a Service is load-balancing traffic
```
kubectl describe services/$SERVICE_NAME
```
## Set a different desired amount of replicas for a deployment
```
kubectl scale deployments/$DEPLOYMENT_NAME --replicas=$DESIRED_NUM_OF_REPLICAS
```
# Tutorials
https://kubernetes.io/docs/tutorials/hello-minikube/
https://kubernetes.io/docs/tutorials/kubernetes-basics/

13
Picking a Linux Distro.md Normal file
View File

@ -0,0 +1,13 @@
# Picking a Linux Distro
## What is a Linux distro?
A Linux Distro (short for distribution) is an operating system built around the Linux kernel. The Linux kernel is the core of every Linux distribution, and facilitates interactions between hardware and software components, as well as handling startup and I/O. The Linux kernel is modular, meaning that it can have modules loaded and unloaded at runtime, instead of modules needing to be built into the kernel at compile time. A kernel module is an object file that contains code that extends the functionality of the kernel. They can provide many different kinds of functionality, but are usually drivers. A Linux distro is a preconfigured distribution of Linux that may include modules for common drivers, as well as a desktop environment and other utilities needed for day to day use. One of the major defining distinctions between Linux distros is the choice of package manager. A package manager is a tool that allows users to manage the installation, updating, and removal of packages (these can be applications, drivers, or any piece of software), and is one of the key defining differences between different Linux distributions.<br>
## Overview of the different major Linux distros
There are 3 major "base" Linux distros, with the defining difference between them being the choice of package manager. If you're just starting out, it's recommended to pick something built from one of these 3 options. You'll find more support, a more active community, and more documentation.
### Debian
Debian is one of the oldest Linux distros, and is **stable release**. This means that all major changes to the operating system will happen with a new release, every two years.
### Arch
### RHEL

View File

@ -0,0 +1,29 @@
This post mostly serves as a collection of extremely useful resources that I find convenient to group together
# Homelabbing
For most of these, you'll need a dedicated server. A used optiplex, or Raspberry Pi or alternative is fine.
Ideally, you'd find something from this list that's actually useful to you.
<https://github.com/awesome-selfhosted/awesome-selfhosted>
# Software Development
I specifically enjoyed reading through [this](https://os.phil-opp.com/) series explaining how to write an operating system. You don't need to understand Rust for the theory section of it.
<https://github.com/codecrafters-io/build-your-own-x>
# Learning
These are all various blogs/series that I enjoyed (not fully inclusive) and found very informative:
- ($TAG, $OTHERTAG) - $NAME: $LINK
- (Operating System development, optimizing, reverse engineering, programming) - Asahi Linux Blog: <https://asahilinux.org/blog/>
- (computer design, low level, back to basics) - Ben Eater's 6502 computer series: <https://eater.net/6502>
- (misc, programming) - FasterThanLime's blog series: <https://fasterthanli.me/>
- (reverse engineering, usb protocol, drivers) - LLBP Reverse Engineering a USB Keyboard: <https://www.youtube.com/watch?v=is9wVOKeIjQ>
- (linux, history) - LWN, Birth of Linux: <https://lwn.net/Articles/928581/>
- (misc, programming) - Corecursive podcast (transcripts available): <https://corecursive.com/>
- (graphics APIs) - I want to talk about webGPU (andi): <https://cohost.org/mcc/post/1406157-i-want-to-talk-about-webgpu>
- (programming, design, thinking, code quality) - The Programmer's Stone: <https://www.datapacrat.com/Opinion/Reciprocality/r0/index.html>
- (optimization) - Massively cutting GTA load times: <https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times-by-70/>
- (hacking, security, reverse engineering) - Hacking my car's firmware: <https://programmingwithstyle.com/posts/howihackedmycar/>
- (optimizing, programming, low level) - doom on a raspi pico: <https://kilograham.github.io/rp2040-doom/>
- (optimizing, operating at scale, programming) Discord Engineering Blog (specifically performance, storing messages): <https://discord.com/category/engineering>

1
Untitled.canvas Normal file
View File

@ -0,0 +1 @@
{}