mirror of
https://github.com/ItsDrike/itsdrike.com.git
synced 2024-11-09 21:49:41 +00:00
Fix formatting in various posts
This commit is contained in:
parent
523fefed1b
commit
c5b1c9da0a
|
@ -89,7 +89,7 @@ Consider this code:
|
|||
```
|
||||
|
||||
In the example here, we can see that python keeps a reference count for the empty list object, and in this case, it was
|
||||
3. The list object was referenced by a, b and the argument passed to `sys.getrefcount`. If we didn't have locks,
|
||||
\3. The list object was referenced by a, b and the argument passed to `sys.getrefcount`. If we didn't have locks,
|
||||
threads could attempt to increase the reference count at once, this is a problem because what would actually happen
|
||||
would go something like this:
|
||||
|
||||
|
@ -167,6 +167,7 @@ become incorrect in a way that's hard to see during code reviews.
|
|||
## Debugging multi-threaded code
|
||||
|
||||
As an example, this is a multi-threaded code that will pass all tests and yet it is full of bugs:
|
||||
|
||||
```python
|
||||
import threading
|
||||
|
||||
|
@ -183,6 +184,7 @@ for _ in range(5):
|
|||
threading.Thread(target=foo).start()
|
||||
print("Finished")
|
||||
```
|
||||
|
||||
When you run this code, you will most likely get a result that you would expect, but it is possible that you could also
|
||||
get a complete mess, it's just not very likely because the code runs very quickly. This means you can write code
|
||||
multi-threaded code that will pass all tests and still fail in production, which is very dangerous.
|
||||
|
@ -192,6 +194,7 @@ behind every instruction to ensure that it is safe if a switch happens during th
|
|||
it is advised to run the code multiple times because there is a chance of getting the correct result even with this
|
||||
method since it always is one of the possibilities, this is why multi-threaded code can introduce a lot of problems.
|
||||
This would be the code with this "fuzzing" method applied:
|
||||
|
||||
```python
|
||||
import threading
|
||||
import time
|
||||
|
@ -219,6 +222,7 @@ for _ in range(5):
|
|||
threading.Thread(target=foo).start()
|
||||
print("Finished")
|
||||
```
|
||||
|
||||
You may also notice that I didn't just add `fuzz()` call to every line, I've also split the line that incremented
|
||||
counter into 2 lines, one that reads the counter and another one that actually increments it, this is because
|
||||
internally, that's what would be happening it would just be hidden away, so to add a delay between these instructions
|
||||
|
@ -226,6 +230,7 @@ I had to actually split the code like this. This makes it almost impossible to t
|
|||
problem.
|
||||
|
||||
It is possible to fix this code with the use of locks, which would look like this:
|
||||
|
||||
```python
|
||||
import threading
|
||||
|
||||
|
@ -257,6 +262,7 @@ for t in worker_threads:
|
|||
with printer_lock:
|
||||
print("Finished")
|
||||
```
|
||||
|
||||
As we can see, this code is a lot more complex than the previous one, it's not terrible, but you can probably imagine
|
||||
that with a bigger codebase, this wouldn't be fun to manage.
|
||||
|
||||
|
@ -264,6 +270,7 @@ Not to mention that there is a core issue with this code. Even though the code
|
|||
bugs, it is still wrong. Why? When we use enough locks in our multi-threaded code, we may end up making it full
|
||||
sequential, which is what happened here. Our code is running synchronously, with huge amount of overhead from the locks
|
||||
that didn't need to be there and the actual code that would've been sufficient looks like this:
|
||||
|
||||
```python
|
||||
counter = 0
|
||||
print("Starting")
|
||||
|
@ -273,6 +280,7 @@ for _ in range(5)
|
|||
print("----------------------")
|
||||
print("Finished")
|
||||
```
|
||||
|
||||
While in this particular case, it may be pretty obvious that there was no need to use threading at all, there are a lot
|
||||
of cases in which it isn't as clear and I have seen some projects with code that could've been sequential but they were
|
||||
already using threading for something else and so they made use of locks and added some other functionality, which made
|
||||
|
|
|
@ -58,18 +58,22 @@ youtube-dl, download the video and then stream it from our machine instead of fr
|
|||
download the file from our server that has now downloaded this video, however that's way too crude.
|
||||
|
||||
There is a much nicer method that we can use, and it is still utilizing pure SSH:
|
||||
|
||||
```sh
|
||||
ssh -f -N -D 1080 user@server
|
||||
```
|
||||
|
||||
This command will start SSH in background (`-f`), it won't run any actual commands (`-N`) and it will be bound to the
|
||||
port 1080 on our machine (`-D`). This means that we can utilize this port as a SOCK and make our server act as SOCKS5
|
||||
proxy. This kind of proxy will even be supported by most web browsers, allowing you to simply specify the address
|
||||
(in our case `127.0.0.1:1080`) and have all traffic go through this external server.
|
||||
|
||||
To test that this connection really does work, we could use the `curl` command like this:
|
||||
|
||||
```sh
|
||||
curl --max-time 3 -x socks5h://127.0.0.1:1080 https://itsdrike.com
|
||||
```
|
||||
|
||||
If we see the HTML code as the output, it means that we've obtained the content of the specified website through our
|
||||
socks5 proxy, that we've established through simple SSH.
|
||||
|
||||
|
@ -93,9 +97,11 @@ around SSH and it will simply utilize SSH in the background, which is also why w
|
|||
server side for this to work properly, as long as we simply have the SSH server running, `sshuttle` will work fine.
|
||||
|
||||
We can use sshuttle with a command like this:
|
||||
|
||||
```sh
|
||||
sudo sshuttle -r user@machine 172.67.161.205/24 -vv
|
||||
```
|
||||
|
||||
Which will forward all traffic destined for the particular address block (the IP/number is called the CIDR notation, it
|
||||
essentially specifies which IPs should be affected depending on the number after /, you can read more about it on
|
||||
[wikipedia](https://wikiless.org/wiki/Classless_Inter-Domain_Routing?lang=en)). In this case, I've specified the IP of
|
||||
|
@ -112,12 +118,14 @@ you need to think about this ahead of time.
|
|||
|
||||
You could also simply redirect the port 22 to something else using iptables instead of having to mess with the SSH
|
||||
config. You would do that with this command:
|
||||
|
||||
```sh
|
||||
sudo iptables -t nat -I PREROUTING -p tcp --dport 1234 -j REDIRECT --to-ports 22
|
||||
```
|
||||
|
||||
This command will make port `1234` act as the SSH port, and you could then access the server by specifying this port
|
||||
instead of the default port in the ssh command:
|
||||
|
||||
```
|
||||
ssh -f -N -D 1080 user@server -p 1234
|
||||
```
|
||||
|
@ -213,9 +221,11 @@ Turns out that even with a security measure as strict as only allowing access to
|
|||
somewhat make our way to our server, by essentially telling it to map all exiting traffic from port 443 to port 22.
|
||||
|
||||
To do this, we would use a command like this:
|
||||
|
||||
```sh
|
||||
ssh -o "ProxyCommand nc -X connect -x proxy_server:3128 our_server_IP 443" user@our_server_IP
|
||||
```
|
||||
|
||||
Here we're essentially sending a proxy command to the web proxy server (listening on port 3128) to through the port 443
|
||||
to our_server_IP and make requests to the SSH's default port (22) on our_server_IP. Making the actual proxy server
|
||||
access our server on port 22.
|
||||
|
@ -235,8 +245,10 @@ really be possible.
|
|||
|
||||
To explain how easy it is to discover something like this, basically all that's needed is to run a single command on
|
||||
that web proxy:
|
||||
|
||||
```sh
|
||||
iptables -t nat -L
|
||||
```
|
||||
|
||||
And look for the output policy destinations. Even though many network admins won't do this, you shouldn't ever risk
|
||||
doing something silly like this, because if you will get discovered, you could get into some serious trouble
|
||||
|
|
|
@ -78,10 +78,9 @@ configured account, you can disable it with:
|
|||
```bash
|
||||
git config --local commit.gpgsign false
|
||||
```
|
||||
|
||||
{{< /notice >}}
|
||||
|
||||
|
||||
|
||||
## Git credentials
|
||||
|
||||
User configuration is one thing, but there's another important part of account configuration to consider, that is
|
||||
|
@ -239,7 +238,6 @@ The cache credential helper will never write your credential data to disk, altho
|
|||
Unix sockets. These sockets are protected using file permissions that are limited to the user who stored them though,
|
||||
so even in multi-user machine, generally speaking, they are secure.
|
||||
|
||||
|
||||
#### Custom credential helpers
|
||||
|
||||
Apart from these default options, you can also use [custom
|
||||
|
|
|
@ -144,4 +144,3 @@ this byte-code with an interpreter. This is also known as Just In Time (JIT) com
|
|||
|
||||
Most languages tend to only be one or the other, but there are a fair few that follow this hybrid implementation, most
|
||||
notably, these are: Java, C#, Python, VB.NET
|
||||
|
||||
|
|
|
@ -36,10 +36,10 @@ systems is, as the name would imply, manage the database. It controls how the da
|
|||
if there should be some internal compression of this database or things like that.
|
||||
|
||||
Even though there are a lot of choices for DBMS, no matter which one we end up using, on the surface, they will be
|
||||
doing exactly the same thing. Storing tables of data. Each item in the database usually has some *primary key*, which
|
||||
doing exactly the same thing. Storing tables of data. Each item in the database usually has some _primary key_, which
|
||||
is a unique identifier for given column of data. We can also have composite primary keys, where there would be multiple
|
||||
slots which are unique when combined, but don't necessarily need to be unique on their own. We can also use what's
|
||||
called a *foreign key*, which is basically the primary key of something in another database table, separate from the
|
||||
called a _foreign key_, which is basically the primary key of something in another database table, separate from the
|
||||
current one, to avoid data repetition. This would be an example of the data tables that a database could hold:
|
||||
|
||||
Table of students:
|
||||
|
@ -68,8 +68,8 @@ Student Grades:
|
|||
| ... | ... | ... | ... |
|
||||
{{< /table >}}
|
||||
|
||||
Here we can see that the *Student Grades* table doesn't have a standalone unique primary key, like the Students table
|
||||
has, but rather it has a composite primary key, in this case, it's made of 3 columns: *Student*, *Subject* and *Year*.
|
||||
Here we can see that the _Student Grades_ table doesn't have a standalone unique primary key, like the Students table
|
||||
has, but rather it has a composite primary key, in this case, it's made of 3 columns: _Student_, _Subject_ and _Year_.
|
||||
We can also see that rather than defining the whole student all over again, since we already have the students table,
|
||||
we can instead simply use the Student ID from which we can look up the given student from this table
|
||||
|
||||
|
@ -148,4 +148,3 @@ Another use-case for databases is when you need to host the data of the database
|
|||
database, we can simply expose some port and let the DBMS handle interactions with it when our program can simply be
|
||||
making requests to this remote server. This is usually how we handle using databases with servers, but many client-side
|
||||
programs are creating their own local databases and using those, simply because using files is ineffective.
|
||||
|
||||
|
|
|
@ -134,8 +134,8 @@ return list(result.values())
|
|||
To preserve the original elements, we used a dict that held the unique hashable memory ids of our objects as keys and
|
||||
the actual unhashable objects as values. Once we were done, we just returned all of the values in it as a list.
|
||||
|
||||
The result of this would be: `[x, y, 1, 2, "hi", Foo(x=5)]`. *(Note that `x`, `y` and `Foo(x=5)` would actually be
|
||||
printed in the same way, since they're the same class, sharing the same `__repr__`)*. From this output we can clearly
|
||||
The result of this would be: `[x, y, 1, 2, "hi", Foo(x=5)]`. _(Note that `x`, `y` and `Foo(x=5)` would actually be
|
||||
printed in the same way, since they're the same class, sharing the same `__repr__`)_. From this output we can clearly
|
||||
see that even though `x`, `y`, and `Foo(x=5)` are exactly the same thing, sharing the same attributes, they're
|
||||
different objects and therefore they have different memory ids, which means our algorithm didn't remove them, however
|
||||
there is now only one `x`, because the second one was indeed exactly the same object, so that did get removed.
|
||||
|
@ -217,4 +217,3 @@ one if we know which classes will be used there.
|
|||
Even though we do have ways to deal with unhashables, if you're in control of the classes, and they aren't supposed to
|
||||
be mutable, always make sure to add a `__hash__` method to them, so that duplicates can be easily removed in `O(n)`
|
||||
without any complicated inconveniences.
|
||||
|
||||
|
|
|
@ -165,7 +165,6 @@ Here's a list of some definable generic types that are currently present in pyth
|
|||
| Mapping[str, int] | Mapping from `str` keys to `int` values (immutable) |
|
||||
{{< /table >}}
|
||||
|
||||
|
||||
In python, we can even make up our own generics with the help of `typing.Generic`:
|
||||
|
||||
```python
|
||||
|
|
Loading…
Reference in a new issue