
Short answer: In a Rails application you can keep an RSS feed fresh with Sidekiq by writing one job that renders the feed from the newest published records and replaces a static file, running it on a schedule with sidekiq-cron or a similar scheduler and also enqueuing it after an article is saved and committed. Make the job idempotent, protect it against overlaps, write the file atomically and monitor its age. The finished address is an ordinary feed that PostRSS can read and post to your social networks.
Rails has excellent built-in support for feeds. A controller can respond with an RSS template, and many applications do exactly that. The trouble starts when the feed becomes popular or the query becomes expensive: every request by a feed reader, a search engine bot or an auto-posting service runs the query and renders the template again. A background job removes that load. It builds the feed once per change, writes a static file and lets the web server deliver it. Sidekiq, the most common background processor in the Rails world, is a natural place to run that job. This guide shows the whole design: the template, the job, the schedule, the trigger after commit, locking, atomic writes, monitoring and the traps that are specific to Rails.
A controller action is the shortest path to a feed, and for a small site it is perfectly fine, especially with caching. A job has three advantages. It decouples the cost of rendering from the number of requests, so a thousand fetches cost the same as one. It lets you build the feed from a heavy query without risking slow responses. And it keeps the public file available when the application is restarting or the database is briefly unavailable, because the web server simply keeps serving the last complete file.
Rails ships with the Builder library, and an RSS template is a short file. Put it in a views folder for feeds and keep it free of business logic.
xml.instruct! :xml, version: "1.0", encoding: "UTF-8"
xml.rss version: "2.0" do
xml.channel do
xml.title "Example News"
xml.link "https://example.com/"
xml.description "Latest articles from Example"
xml.lastBuildDate Time.current.rfc2822
@articles.each do |article|
xml.item do
xml.title article.title
xml.link article_url(article)
xml.guid article.guid, isPermaLink: false
xml.pubDate article.published_at.rfc2822
xml.description article.summary
xml.enclosure url: article.image_url, type: "image/jpeg" if article.image_url.present?
end
end
end
endUse absolute links and image addresses, because auto-posting services read the feed from outside your site. Keep the guid stable for the life of the article. Builder escapes text for you, which removes a whole class of invalid-XML problems.
The job loads the newest published articles, renders the template and replaces the file. It takes no arguments, so it does not matter which article triggered it.
class RebuildFeedJob
include Sidekiq::Job
sidekiq_options queue: "feeds", retry: 5
FEED_PATH = Rails.root.join("public", "feeds", "news.xml")
def perform
articles = Article.published.order(published_at: :desc).limit(30)
xml = ApplicationController.render(
template: "feeds/news",
formats: [:rss],
layout: false,
assigns: { articles: articles }
)
write_atomic(FEED_PATH, xml)
end
private
def write_atomic(path, xml)
FileUtils.mkdir_p(File.dirname(path))
tmp = "#{path}.#{Process.pid}.tmp"
File.write(tmp, xml)
File.chmod(0o644, tmp)
File.rename(tmp, path)
end
endThe published scope should filter on status and on a publication time that is not in the future, so that drafts and scheduled articles never appear in a public feed. Writing to a temporary name and renaming is atomic on the same filesystem, so a reader never sees half a file. If your application runs on several servers, write to shared storage or to object storage, not to the local disk of whichever machine happened to run the job.
Sidekiq itself does not include a scheduler, but the sidekiq-cron and sidekiq-scheduler gems are common choices. With a cron-style schedule in a YAML file, the entry is short:
rebuild_feed:
cron: "*/10 * * * *"
class: "RebuildFeedJob"
queue: feedsEvery ten minutes is plenty for most sites. The schedule acts as a safety net even if you also trigger the job on publication. Make sure that only one scheduler instance enqueues the entries, or the job will be enqueued several times per tick. The gems take care of this with a lock, but confirm it in their documentation for your version.
Waiting for the next tick is fine for many blogs, but you can make the feed update within seconds. Enqueue the job from an after-commit callback so that it never starts before the data is visible to the worker.
class Article < ApplicationRecord
after_commit :refresh_feed, on: [:create, :update]
private
def refresh_feed
RebuildFeedJob.perform_in(3.seconds) if published?
end
endThe delay of a few seconds lets the transaction settle and also helps to merge a burst of saves into fewer rebuilds. Do not use after_save for this, because it runs before the commit, and the worker might read old data.
When an editor saves ten articles in a minute, ten jobs reach the queue. Because the job reads the full current state, running them one after another is harmless, but running them in parallel wastes resources and can make two workers rename files at the same moment. Limit the queue to a single thread for feeds, or protect the job with a short lock in Redis, or use a unique-jobs gem that drops duplicates while one is already waiting. A simple approach is a dedicated queue with concurrency of one, because the job is quick and rarely needs parallelism.
Sidekiq retries failed jobs with an increasing delay, which is what you want for a temporary database problem. After the retries are exhausted, the job moves to the dead set, where you can inspect and retry it. Configure an error tracker to notify you when a feed job dies, and do not swallow exceptions inside the job. A job that rescues everything and reports success hides the very failure you need to see. Keep the old file in place until a new one is complete, so a failed run never leaves the public address empty.
Files in the public directory are normally served by your web server, such as Nginx, without touching Rails. Configure the server to send the content type application/rss+xml with a UTF-8 charset and a short cache lifetime for the feeds directory. If you use a content delivery network, remember that it adds a cache layer, so allow for a few minutes of delay or purge the cache after each rebuild. Keep the feed address stable for the life of the site.
Write a test that creates a few articles, including a draft and a future-dated one, runs the job and reads the generated file. Assert that the file parses as XML, that the published articles appear in the right order and that the draft and the future article do not. Add a test for the callback that checks the job is enqueued after commit and not for drafts. A staging check that measures the seconds between saving an article and seeing it at the public address is a good final test, and it catches problems with the queue, the file permissions and the web server.
Track the age of the file and the age of the newest item inside it. Compare the newest item with the newest published article in the database. Sidekiq’s own dashboard shows queue latency and failed jobs, but an external check that fetches the public address and parses it is the most reliable test, because it exercises the whole chain. Alert when the file is older than three times the schedule interval.
Auto-posting services build their posts from the feed, so the quality of the summary and the image matters as much as the structure. Store a short plain-text summary for each article, one or two sentences without markup, and use it as the item description. For the image, expose a full public address of a picture that opens without a login and has a decent size, since social networks crop previews. In Rails, keep that logic in a presenter or a model method, so that the template stays simple and the job does not need to know about storage services or variants.
Many applications need more than one feed: the whole blog, one per category or author, a podcast feed or a feed of products. Do not copy the job for each one. Write a single job that takes a feed key, such as the category slug, looks up the right scope and template and writes to a predictable path. Schedule one entry per feed or enqueue the job with a key from the after-commit callback. Keep the number of items small and the queries indexed, and give each feed its own address that never changes.
When you deploy, make sure the feed directory exists and is writable by the user that runs Sidekiq, and readable by the web server. With container-based deployments, the file system of a container is usually discarded on restart, so write the feed to a mounted volume or to object storage, and rebuild it once after each deployment so that the public address is never empty. Keep the scheduler configuration in version control with the rest of the code. After a deployment that touches the queue, Redis or the web server configuration, run the staging check again and confirm that a new article reaches the public address within the expected time.
It is tempting to add logic over time: special cases for a campaign, extra fields for one reader, a different title format for one category. Resist it. Keep the template a plain description of items, move formatting decisions into small presenter methods and cover them with tests. A feed that stays boring and predictable is a feed that keeps working for years.
PostRSS only needs a valid RSS or Atom address that can be opened without a login. It checks the address, finds items it has not posted before and publishes them, with image and link, to the networks you connect. Your job keeps the file fresh within seconds of publication and at worst every few minutes, so the next check finds the new article. See the features page for the networks, filters and scheduling options, and the pricing page for plans.
A Sidekiq job turns feed generation into quiet background work. Render the feed from the newest published records, replace the file atomically, schedule the job as a safety net, enqueue it after commit for speed, prevent overlaps, let retries handle temporary failures and watch the age of the file from outside. Then give the address to an auto-poster, and every new article reaches your audience without extra work.
No. A controller action with caching is enough for small sites. A Sidekiq job is useful when you want a static file, heavy queries or an immediate rebuild after publication.
It runs after the database transaction is complete, so the job sees the new article. A callback that runs before the commit can rebuild the feed from old data.
Every five to fifteen minutes is enough for most sites. Enqueue it after publication as well if you want new items within seconds.
Sidekiq retries with a growing delay, and the last complete file keeps being served. Alert on dead jobs and on the age of the file.
Yes. PostRSS reads any valid RSS or Atom address that is reachable without a login, however the file is produced.
What changed in the networks, what broke, and how to fix it before it costs you reach.
Creadas por Internet Solutions, el equipo detrás de PostRSS. Cada una te ahorra tiempo a su manera.