More from Notes on software development
Transactions are not an intrinsic part of a storage system. Any storage system can be made transactional: Redis, S3, the filesystem, etc. Delta Lake and Orleans demonstrated techniques to make S3 (or cloud storage in general) transactional. Epoxy demonstrated techniques to make Redis (and any other system) transactional. And of course there's always good old Two-Phase Commit. If you don't want to read those papers, I wrote about a simplified implementation of Delta Lake and also wrote about a simplified MVCC implementation over a generic key-value storage layer. It is both the beauty and the burden of transactions that they are not intrinsic to a storage system. Postgres and MySQL and SQLite have transactions. But you don't need to use them. It isn't possible to require you to use transactions. Many developers, myself a few years ago included, do not know why you should use them. (Hint: read Designing Data Intensive Applications.) And you can take it even further by ignoring the transaction layer of an existing transactional database and implement your own transaction layer as Convex has done (the Epoxy paper above also does this). It isn't entirely clear that you have a lot to lose by implementing your own transaction layer since the indexes you'd want on the version field of a value would only be as expensive or slow as any other secondary index in a transactional database. Though why you'd do this isn't entirely clear (I will like to read about this from Convex some time). It's useful to see transaction protocols as another tool in your system design tool chest when you care about consistency, atomicity, and isolation. Especially as you build systems that span data systems. Maybe, as Ben Hindman hinted at the last NYC Systems, even proprietary APIs will eventually provide something like two-phase commit so physical systems outside our control can become transactional too. Transactions are a protocol short new post pic.twitter.com/nTj5LZUpUr — Phil Eaton (@eatonphil) April 20, 2025
This is an external post of mine. Click here if you are not redirected.
This is an external post of mine. Click here if you are not redirected.
I was so intimidated to go at first, but it is in fact easy and fun to start playing beginner volleyball in New York. The people are so friendly and welcoming that it has been easy to keep playing consistently every week since I started for the first time this August. It's been a great workout and a great way to make friends! The two platforms I've used to find volleyball games are Goodrec and New York Urban. While these platforms may also offer classes and leagues, I mostly use them to play "pickup" games. Pickup games are where you show up and join (or get assigned to) a team to play for an hour or two. Easy to go on your own or with friends. I'm not an expert! My only hope with this post is that maybe it makes trying out volleyball in New York feel a little less intimidating for you! Goodrec With Goodrec you have to use their mobile app. Beginner tier is called "social" on Goodrec. So browse available games until you find one at the level you want to play. You enroll in (buy a place in) sessions individually. Sessions are between 90-120 minutes long. They ask you not to arrive more than 10 minutes early at the gym. When you arrive you tell the gym managers (usually in a desk up front somewhere) you're there for Goodrec and the tier (in case the gym has multiple level games going on at the same time). Then you wait until the Goodrec "host" arrives and they will organize everyone into teams. Goodrec hosts are players who volunteer to organize the games. They'll explain the rules of the game (makes Goodrec very good for beginners) and otherwise help you out. Always say thank you to your host! New York Urban With New York Urban, pickup sessions are called "open play". There is no mobile app, you just use the website to purchase a spot in a session. The sessions are longer and cheaper than Goodrec. But there is no host; players self-organize. The options are more limited too. You play at one of four high schools on either a Friday night or on Sunday. And session slots tend to sell out much more quickly than with Goodrec. Big City Volleyball You can also check out Big City Volleyball but I haven't used it yet. Volo I haven't ever done Volo but I think I've heard it described as "beer league". That even some of the beginner tier sessions with Goodrec and New York Urban are more competitive. But also, Volo is built around leagues so you have to get the timing right. Goodrec's and New York Urban's pickup games make it easy to get started playing any time of year. Making friends It was super awkward to go at first! I went by myself. I didn't know what I was doing. I couldn't remember, and didn't know, many rules. I didn't have court shoes or knee pads. But the Goodrec host system is particularly great for bringing beginners in and making them feel welcome. You have a great time even if you're terrible. The first game I went to, I tried to hang out afterward to meet people. But people either came with their SO or with their friends or by themselves so they all just left immediately or hung out in their group. So you can't just go once and expect to make friends immediately. But if you keep going at the same place and time regularly week over week, you'll see familiar faces. Maybe half the people I play with each week are regulars. If you're friendly you'll start making friends with these people and eventually start going out to bars with them after the games. Improving Even if you find yourself embarrassingly bad at first, just keep going! I'm 29, 6'1, 190lbs and from observation the past 5 months, age, height, and weight have a very indirect relation to playing ability. Most of the people who play are self-taught, especially at the lower tiers I've played at. But some people played for the school team in high school or college. These people are fun to play with and you can learn a lot from them. Most people who are self-taught seem to watch YouTube videos like Coach Donny, helpful for learning how to serve, set, block, etc. Or they take "clinics" (classes) with Goodrec or other platforms. (I have no idea about these, I've never done them before.) At first I played 2 hours a week and I was completely exhausted after the session. Over time it got easier so I started playing 2-3 sessions a week (6-9-ish hours). With practice and consistency (after about 3-4 months), I started playing Intermediate tier with Goodrec and New York Urban. And I don't think I'll play Beginner/Social at all anymore. I still primarily play for fun and for the workout and to meet people. But it's also fun to get better! I played with one person much better than myself in an Intermediate session one time and he mentioned he will probably stop playing Intermediate and only play High Intermediate. He mentioned you get better when you keep pushing yourself to play with better and better players. Good advice!
More in technology
A great deal of effort has gone into understanding the regulatory burden on housing construction, and the extent to which it contributes to high housing prices.
Yes, that's right! Today we're at the aquarium. Welcome back to yet another watchTowr Labs blog post. You can begin preparing your comments now. What Is A Barracuda, And Why? Must We? Barracuda’s Email Security Gateway solution (ESG) is no stranger to love, for
You have an idea. Maybe it’s a smart home device that solves an everyday annoyance, a robot you’ve been sketching for months, or an experiment that becomes something bigger. Now comes your first major decision: what hardware will you use to bring it to life? With so many development boards available, choosing the right one […] The post What will you build next? Arduino Black Friday deals are here appeared first on Arduino Blog.
On Sunday morning, a lot of us woke up to Grok Bot, the always-on AI assistant, decidedly off. “100% used” read my quota meter. It’d been 45% late the night before. It was an error, but of the Freudian slip sort, and that’s a problem if tools like this are to be taken seriously.
A Pika author was editing the same post in two browsers. They added six images in Browser A, then later they referenced an old tab editing the post in Browser B. That copy of the editor did not include those images. Browser B saved and removed all six images from Active Storage. A short time later, Browser A saved its newer copy of the post and put the image references back, but at that point the originally uploaded files were gone due to Browser B saving the post without the images. This exposed two separate problems: Missing images produced uncached 404 responses that the editor requested repeatedly. Action Text purged an image as soon as one save removed it from the body. Here’s how we fixed those problems. Why Action Text deleted the images Action Text keeps a rich text record’s embeds synchronized with the attachments in its body. When a submitted body no longer contains an image, Action Text removes its ActiveStorage::Attachment. Active Storage then purges the dependent blob, deleting both its database record and its file. That behavior makes sense when there is only one current copy of a document. It is unsafe when the same post can be open in multiple tabs, browsers, or devices. One stale save can delete an image that another editor is still using. Why the missing images flooded the server Browser A still had the six images in its editor, each pointing at an Active Storage redirect URL: /rails/active_storage/blobs/redirect/:signed_id/:filename Pika uses the ProseMirror-based Rhino editor. Rhino rebuilt an image’s DOM node when its caption or selection changed. Each new <img> element requested its URL again. Healthy images did not cause trouble since Active Storage makes its redirect cacheable. For a purged blob, Rails returned a 404 without Cache-Control, so the browser kept sending the request to Rails instead of reusing the failure. In a gallery, typing could re-request several missing images per keystroke. In these scenarios, this issue led to hundreds of requests hitting our servers. Fix 1: Cache permanent Active Storage 404s A purged blob’s redirect URL can never start working again. Blob IDs are not reused, and uploading the image again creates a new blob with a new signed URL. Sinc there is no concern of a 404 becoming stale, we made a specific 404 cacheable for the same period as a successful redirect: # app/controllers/concerns/active_storage_missing_blob_cache.rb module ActiveStorageMissingBlobCache extend ActiveSupport::Concern included do rescue_from ActiveRecord::RecordNotFound, with: :render_cacheable_not_found end private def render_cacheable_not_found expires_in ActiveStorage.service_urls_expire_in render file: Rails.public_path.join("404.html"), layout: false, status: :not_found end end We included the concern only in the controller serving these blob redirects: # config/initializers/active_storage_missing_blob_cache.rb Rails.application.config.to_prepare do ActiveStorage::Blobs::RedirectController.include(ActiveStorageMissingBlobCache) end (We also offered an update to Rhino that avoids it being so aggressive re-requesting images.) Fix 2: Detach dropped images instead of purging them When a save removes an image from an Action Text body, we now remove the attachment while temporarily keeping its blob. Another browser can save the image again and reattach that same blob. If nothing brings it back, a cleanup job purges it later. First, we added a timestamp for when the image was dropped: class AddDetachedAtToActiveStorageBlobs < ActiveRecord::Migration[8.1] def change add_column :active_storage_blobs, :detached_at, :datetime end end We then intercepted the private callback Active Storage uses to purge a dependent blob: # config/initializers/detach_dropped_embeds.rb module DetachDroppedEmbeds private def purge_dependent_blob if dropped_from_rich_text? blob&.update_columns(detached_at: Time.now) else super end end def dropped_from_rich_text? record.is_a?(ActionText::RichText) && !destroyed_by_association end end ActiveSupport.on_load(:active_storage_attachment) do prepend DetachDroppedEmbeds end The destroyed_by_association check preserves intentional deletion. Removing an image from a saved body detaches it, but deleting the post and its rich text still purges its images normally. Purge detached blobs after a grace period The cleanup query considers both when the blob was created and when it was detached: ActiveSupport.on_load(:active_storage_blob) do scope :unattached_since, ->(time) { unattached.where( created_at: ..time, detached_at: [nil, ..time] ) } end That scope handles both kinds of unattached blobs: A new upload that has never been attached becomes eligible based on created_at. An image removed from an old post becomes eligible based on detached_at. Pika gives both a 30-day grace period: class PurgeUnattachedUploadsJob < ApplicationJob GRACE_PERIOD = 30.days def perform ActiveStorage::Blob.unattached_since(GRACE_PERIOD.ago).find_each do |blob| blob.purge_later end end end If another browser saves the image during that period, it is attached again and the unattached scope excludes it from cleanup. The result Replaying the original two-browser edit now works safely: Browser B saves a body without the six images. The attachments are removed, but their blobs are only marked as detached. Browser A saves its newer body and reattaches the existing blobs. The images remain available and do not need to be uploaded again. If no editor brings a detached image back, the cleanup job eventually purges it. Much better! Reply by email