Skip to main content

November 22

Hi everyone, hope you’re well! Slightly earlier update than usual, as I had the energy for writing and figured I might as well.

Two factor auth improvements

The two factor auth for ArtCentral is done, but it was lacking any kind of recovery solution if people lost their 2FA device. Setting up mobile texts costs money (not a big amount of money, but for now it seems like more hassle than it’s worth), so instead I created a recovery code system.

Upon enabling 2FA, two one-time recovery codes are now autogenerated using cryptographically secure random bytes and displayed only once to the user (i.e. you can’t go and view them again, for security reasons). These can then be used to log in if a 2FA device is lost, though the user still needs their password.

I still need to implement the actual login process for using them; I’ve been meaning to revamp the login and create account pages anyway, as they’re a mess at the moment and needed sorting out sooner or later.

Distributed session management (Redis)

With Redis integrated for caching search results and other criteria, another important optimisation is possible: storing user sessions in Redis as well. Doing so is fairly simple to do, and comes with an extra benefit: by storing some user information inside sessions (such as user profile settings and such, but never credentials of any kind), a lot of pages that require this information no longer require database calls and can get the information directly from the session and thus from Redis.

This meant a little refactoring, as in order for user information in a session to be up to date, it must be reloaded from the database if a user changes their settings on one of the settings pages, but that’s all sorted.

Another side bonus of using Redis for session management is that ArtCentral’s load balancer will no longer need to use sticky sessions. In production, the webservers will connect to one load balancer which is responsible for distributing web traffic. Without a centralised session handler (e.g. if each webserver is handling sessions via filesystem, the PHP default), ‘sticky sessions’ are needed to ensure that when a user browses the website, the load balancer always directs them to the same webserver (or else their session would be lost and confused).

With all sessions going through Redis instead, there is no longer a need for sticky sessions on the load balancer, since individual webservers are not keeping sessions anymore. This means the load balancer can direct users to whichever webserver has the least load, rather than making a user use the same webserver for their whole session. On a site like ArtCentral where session timeouts are pretty long (as there’s no real security reason for them to be short, it’d just annoy people), that’s a big improvement.

Displaying artworks

For the main “display one artwork” page, I realised it won’t always be ideal for portrait-orientation artworks to be confined to the height of the screen. Instead, they can now fill more than the height of the screen, and be scrolled up and down. What I might do is try to expand this to have a zoom-in or zoom-out option, so that it’s clear to users when an image is scrollable or not (since some images might not immediately look like there’s more to see).

This led to me realising a rather impactful bug in my image resizing code, which is detailed below.

Image resizing and compression

Resizing

So first of all, resizing. I realised that the code I’ve currently got deployed was resizing ALL images - irrespective of orientation - to the given widescreen format resolution. For example, if you uploaded an artwork in 16:9 format (let’s say it was 2560x1440), it would be resized to 1920x1080. However, if you uploaded an artwork in 9:16 format (1440x2560), it would get resized to 607x1080 - as it was being constrained to the widescreen resolution.

I’ve fixed this now, but it has a relatively large side effect: thumbnails were also being generated this way, meaning that both portrait-orientation 1080p images and their thumbnails are now often larger and thus of larger filesize. This meant updating my financial estimates, and it’s probably worth me running more tests to determine the average likely thumbnail size with this new information.

Right now, thumbnails are constrained to a maximum of 640x480 or 480x640, which for a thumbnail is pretty big. I’m considering reducing this as large numbers of thumbnails in galleries make for a high proportion of total download costs, and they are rarely actually displayed at that size in galleries while testing; there’s definitely some optimisation to be done there, and the thumbnail cost is bigger than for any other images so it’s well worth getting right.

The two things I need to do to test that correctly are to first figure out what the cost savings are for some smaller sizes, and importantly, check what the maximum likely sizes for galleries are. It’s difficult to determine due to the kind of gallery implementation I have, but there’s still plenty of room for optimisation.

Compression

After doing a lot of testing, and looking more into the noticeable differences between 80 and 90% JPEG compression quality, I’ve decided to increase artworks at 1080p and 4k to 90% quality. Thumbnails will remain at 80%; it is impossible to notice on a thumbnail, they are not expected to be zoomed in on, and they are the biggest cost; increasing them to 90% would be both unnecessary and massively expensive.

I’ve adjusted my financial estimates to account for this as well.

Todo: extra redis optimisations

There’s still some extra Redis optimisations to make, but I have to decide how to do them. For example, displaying next/previous artworks in a user gallery is easy enough to cache, but there is no easy or efficient way to e.g. only invalidate one Redis result when a user adds a new artwork.

Theoretically, if a user adds a new artwork, you only need to invalidate the next/previous results for the last artwork that was the newest, as it’s the only one for which the results will change. However, it’s proven to be impractical to do this; either the whole Redis cache of previous/next artwork links for that user need to be invalidated or none at all.

It’s also not necessarily better to do this; it’ll save database time but add considerable memory usage to Redis, and Redis memory is not exactly a huge resource, so I need to decide if it’s actually worthwhile. It *should* be, since this is something where the result can be cached forever until the user changes their gallery, but I need to investigate it a little more.

Current project outlook

Things are going well, so far. A fair bit of the site is ‘working’, in terms of being an art gallery, but there are still a lot of bugs to fix and a lot to do - notably big things to implement will be notifications and private messages. I also want to make a feature for a sort of ‘retweet’ function, but internal - so that instead of e.g. simply following an artist or user, you can also opt to follow their ‘retweets’ instead - a way of sharing art efficiently that does not really exist on other art sites that I know of. I still need to come up with an appropriate name for that action, figure out how to structure it internally, and decide on things like whether text additions should be allowed (from a moderation standpoint that’s a nightmare, so I highly doubt it, but there might be some pre-defined allowed additions to avoid that problem).

Right now, I am hoping to have things mostly ready by the middle of 2023, but it remains to be seen how optimistic that is. With any luck I might even get it done earlier, but we will see. Mostly it’s longer than I originally thought due to the addition of this ‘retweet’ system, which might take a little while to figure out, and just the sheer number of little bits and pieces that need to be completed.