# Scaling Nextdoor's Datastores: Part 3

DevFeed: [Scaling Nextdoor's Datastores: Part 3](<https://devfeed.tech/articles/scaling-nextdoor-s-datastores-part-3-20342.md>)

Original publisher: [Read original article](<https://engblog.nextdoor.com/scaling-nextdoors-datastores-part-3-e9b4dd8a9393?source=rss----5e54f11cdfdf---4>)

Author: Ronak Shah

Published: 2025-03-19T15:08:43Z

Content type: article

Language: en

Sources: [Nextdoor](<https://devfeed.tech/sources/nextdoor.md>)

Topics: [Caching](<https://devfeed.tech/topics/caching.md>), [Databases](<https://devfeed.tech/topics/databases.md>), [schema-evolution](<https://devfeed.tech/topics/schema-evolution.md>), [Object-relational mapping](<https://devfeed.tech/topics/orm.md>), [Django](<https://devfeed.tech/topics/django.md>), [Python](<https://devfeed.tech/topics/python.md>), [Redis](<https://devfeed.tech/topics/redis.md>), [valkey](<https://devfeed.tech/topics/valkey.md>)

Tags: [blog](<https://devfeed.tech/tags/blog.md>), [cache](<https://devfeed.tech/tags/cache.md>), [cache-invalidation](<https://devfeed.tech/tags/cache-invalidation.md>), [caching](<https://devfeed.tech/tags/caching.md>), [compatibility](<https://devfeed.tech/tags/compatibility.md>), [database](<https://devfeed.tech/tags/database.md>), [django](<https://devfeed.tech/tags/django.md>), [lua](<https://devfeed.tech/tags/lua.md>), [python](<https://devfeed.tech/tags/python.md>), [redis](<https://devfeed.tech/tags/redis.md>), [schema](<https://devfeed.tech/tags/schema.md>), [serialization-format](<https://devfeed.tech/tags/serialization-format.md>), [thundering-herd](<https://devfeed.tech/tags/thundering-herd.md>), [valkey](<https://devfeed.tech/tags/valkey.md>)

## AI overview

Part 3 of Nextdoor's datastore-scaling series explains how applications serialize database objects for Redis or Valkey look-aside caches. It describes compatibility problems caused by runtime, package, and schema changes, including cache misses and thundering-herd effects during migrations.

## Source excerpt

In this part of the Scaling Nextdoor's Datastores blog series, we'll explore how the Core-Services team at Nextdoor serializes database data for caching while ensuring forward and backward compatibility between the cache and application code. In part 1 of this series we discussed how ORMs, object-relational mapping frameworks, help abstract away database specific schemas and queries from application code. Developers simply utilize objects in their application's language to access database data. Here's a simple example of using Python's Django ORM to define a model: from django.db import models class Users(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) The associated SQL create table would look like: CREATE TABLE users ( "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL ); Developers would then access database data like this: user_id = 123 user = User.objects.get(id=user_id) print(user.first_name)Object Byte Serialization for Caching An issue arises when adding a look-aside cache such as Redis/Valkey to an application: How do you store what you got from the database in the cache? A common solution to caching complex objects, such as those from ORMs, is object byte serialization. This process converts language objects into bytes before storing them in the cache. When reading from the cache the process is done in reverse where the byte data is turned into language objects. For instance in Python this is often done with the pickle package. The interaction between the application, database, and the cache looks like this: Look-Aside Cacheimport pickle # Try getting from cache ('None' if not in cache) user_bytes = cache.get("user_123") if user_bytes is not None: # Read bytes using pickle user = pickle.loads(user_bytes) else: # Fetch from database user = User.objects.get(id=123) # Convert to bytes user_bytes = pickle.dumps(user) # Sto