- “Today, Gemini reached an agreement in principle with Genesis Global Capital, LLC (Genesis), Digital Currency Group, Inc. (DCG), and other creditors on a plan that provides a path for Earn users to recover their assets. This agreement was announced in Bankruptcy Court today. This plan is a critical step forward towards a substantial recovery of assets for all Genesis creditors. In addition, Gemini will be contributing up to $100 million more for Earn users as part of the plan, further demonstrating Gemini’s continued commitment to helping Earn users achieve a full recovery.”
- There are 18 EGOT winners.
- YouTubeTV is $65/mo.
- Remember by default, containers build/run as root, and execute processes within as root. You can change these to a specific user for even more control/security.
- Temporary issues with local servers for
archive.ubuntu.com:http:*
today, apt was installing packages incredibly slowly (or failing). - Couldn’t find a way to unsubscribe from uber email notifications (they duplicate all the time).
- “Google Invests Almost $400 Million in ChatGPT Rival Anthropic”
- The BMW M1000RR is so sick. 205hp, 420lbs, 4cyl inline, 1000cc, $38k MSRP.
- For reference: The HP4 was 215hp, 320lbs, 4cyl inline, 1000cc. It was around $85k IIRC. Fully carbon. Not street legal, track only. They only made 750 units in 2017.
- You can nest executions within an f-string, like
f"{foo:{bar}}"
(eg dynamically padding one variable with spaces according to a width set by another variable).
- Airthings masters kicked off today.
- Champions chess tour page got a rewrite, looks better (much more like chess.com now that it bought chess24/CCT): https://championschesstour.com/schedule/
- They changed the rules *substantially* this year: https://championschesstour.com/format-regulations/
- 6 events the tour finals, each having a play-in and then a knockout. $2m total fund.
- Play-ins are 10+2 9-round swiss, knockouts are 15+3 double match elimination (loser bracket winner must beat winners bracket winner twice to win).
- There are 3 completely separate divisions in knockouts. Div 1 has 8 players and knockout matches are 4 games in the winners bracket and 2 games in the losers bracket. Div 2 has 16 players and knockout matches are 4 games in the winners bracket and 2 games in the losers bracket. Div 3 has 32 players and knockout matches are 2 games in both brackets, with the exception of the final being 4 games.
- Placement in knockout divisions is according to rank in last event’s knockout as well as rank in this event’s play-in. There are weekly qualifiers for 3 non-GMs to get into the play-in.
- SBSC. Switched from uwsgi to gunicorn and moved the proxy infra to ELB.
- https://gitlab.com/bmahlstedt/supercontest/-/issues/186
- Requested a new certificate from ACM, verified via DNS (you have to click the button to create the CNAME records in route53 to prove you own that domain).
- ACM public certs are free. Target groups are free, obviously. The load balancer itself charges on use/traffic.
- Sidenote – accidentally deleted privateer’s cert, had to reissue from amplify (remove-readd domain).
- No longer need the
infra
containers.nginx-proxy
andletsencrypt
are gone. - Python apps speak wsgi (web server gateway interface), of course, but most proxies nowadays can speak this as well (including nginx). They’re not just static file servers nowadays.
- Remember sockets are less overhead than ports.
uwsgi
has a stats server but I don’t use it: https://uwsgi-docs.readthedocs.io/en/latest/StatsServer.html- You can actually embed your entire app in the uwsgi binary! https://uwsgi-docs.readthedocs.io/en/latest/Embed.html. But still easiest to have the single artifact be a docker image.
- Docker comes with 3 networks predefined that can’t be removed: bridge, host, none.
- Was getting 502 bad gateway in prod. Traced to uwsgi segfaults. Tested and isolated to
SC_DEV
. In flask debug=False mode, something was breaking with uwsgi. Hence the switch to gunicorn. - Also from the uwsgi docs: “The project is in maintenance mode (only bugfixes and updates for new languages apis). Do not expect quick answers on github issues and/or pull requests (sorry for that) A big thanks to all of the users and contributors since 2009.” And gunicorn is more popular, more support.
- I ended up still fronting gunicorn with an nginx proxy, but now it’s a simple
nginx
base image that I copy a custom config in for. No more third-party dependencies on jwilder and letsencrypt. - SSL is now handled by ELB, which has a cert from ACM attached to the HTTPS listener. You can’t just associate a cert with an EC2 instance. You also can’t copy a cert generated from ACM into your nginx config. You just front with ELB, hence the change. Autorenewal.
- Workers and threads (I’m using gthreads in gunicorn, not sync) are 2*NUM_CORES+1. This is set dynamically, and can read fine in both WSL2 and EC2.
- Remember that although each gunicorn worker starts my flask app in a separate process, instantiating a new apscheduler, they all share a centralized store (no duplicate scheduled jobs).
- Here are the default variables that flask injects into the global jinja context: config, request, session, g, url_for(), get_flashed_messages(). I use all.
- Docker creates a bridge network by default, and exposes it to the internet. The
external
key is not for connectivity. It just means that the network is managed outside of that compose file. expose
means it’s available to other containers in docker-compose.ports
means it’s available to the host (and therefore anything that can access that host, which for a webserver, is the whole internet).- Best toplevel docs for nginx: https://nginx.org/en/docs/dirindex.html and https://nginx.org/en/docs/varindex.html
- Nginx returns a 302 redirect for my reverse-proxy-to-gunicorn-container config, so I had to tell AWS that the target group considers both 200 and 302 as successful. Otherwise the health check will declare failure, and the load balancer won’t forward requests to an unhealthy target.
- Full path:
- Route53 sees the domain request and has an A record to send to ELB.
- ELB has a listener on HTTPS/443 and an associated SSL cert from ACM.
- The listener forwards the decrypted requests to a target group configured to receive HTTP/80.
- The target group forwards to port 80 on the EC2 instance (within the VPC).
- That EC2 instance is running an nginx container, which forwards 80 from the host to 80 within the container.
- Nginx proxy passes the traffic to the exposed port 8000 on the gunicorn container.
- The gunicorn container handles the request (passes to my application, defined in the flask microframework).
- The load balancer is governed by a security group that allows all HTTP/S traffic from all IPv4 addresses.
- The EC2 instance is governed by a security group that allows HTTP traffic from only the load balancer (security group), as well as ssh (from anywhere) for dev.
- Kept an A record in route53 to the elastic IP so that I could ssh to a static address for convenience (my manual work, ansible, etc).
- Remember docker compose will add the name of the services to the network so they’re accessible by other containers in the same network. Eg: My compose file defines
supercontest-app
, and the nginx container can reach it athttp://supercontest-app
. - Health checks to a target group do not count as load balancer capacity units. You’re not charged for these.
- SBSC. Redid all stream management into a proper
logging
implementation. - All print() calls gone.
- Every sbsc module logs individually.
- Inspects the logging namespace and handles other loggers too.
- Strips the gunicorn loggers, which have a stream handler which swallows everything, and instead has them propagate up to the root logger (like everything else).
- I then manage the streamhandler and formatter on that, giving full control.
- SBSC. Other.
- Sped up the dockerfiles. It now copies the pyproject toml and poetry lock ONLY into the container before install and the whole source dir, so that docker can cache it and only pip reinstall (the long part) when the lock/toml change.
- Reorganized the dockerfiles and server configs. All are in their appropriate subdirs now, and build contexts are relatively defined.
- Significantly cleaned up the app factory pattern of
init.create_app()
. It now splits by cli-mode, dev-mode, and prod. Lazy importing only when necessary. Dynamically logging. - This cleanup fixes a lot. Example: the flask cli runs your factory to access your app. So it prints everything, sets up caching/minification/scheduler/etc – all the stuff it doesn’t need. That’s now fixed.
- This means
flask shell
andflask db <>
are both faster, and don’t log garbage to console.