jPrime 2026

Spring Data Redis Beyond Key-Value, Viktoriya Kutsarova

39:42 · 03 Jun 2026 – 04 Jun 2026 · YouTube

About this talk

This talk introduces Spring Data Redis, a powerful extension of the Spring Data family that facilitates integration with the Redis datastore. The speaker, Victoria, explains its evolution beyond simple key-value storage to support more complex use cases through new APIs. She illustrates various real-world scenarios, starting with a workshop reservation system where holds can be created, extended, and consumed using newly implemented hash data structures that allow individual expirations for each field. Then, she discusses managing live speaker Q&A through Redis Streams, demonstrating how the new APIs streamline operations by reducing the required number of calls for appending questions and acknowledging responses. Finally, she touches on upcoming features in JSON support and advanced search capabilities, emphasizing the move towards more efficient, atomic operations on the server side that lessen the burden on application code.

Full transcript

Good afternoon. You can hear me well. I'm Victoria. Uh just to tell you that this is my first tech talk. So please don't be too harsh on me. Uh today we are going to talk about Spring Data Reads and Spring Data Read has been moving well beyond key value lately. [snorts] Uh and that's exactly the topic we are going to cover. So let's start. Let's just do

a quick poll. Um, raise your hand to answer. So, who here has used Radius? Okay, more than half of the room. That's great. And who here has used Spring Data Radius itself? Okay, a maybe a little bit less, but still great to see that you're familiar with parts of the ecosystem. Now, let me introduce myself. I am Victoria with a K, not a C and with a

Y, blame the Bulgarian transliteration of the early 2000s. I'm part of the clients and ready uh of the clients and ecosystems team at Radius. And my first contribution to Spring Data Radius was in November 2025 and I've been working on it since then. So, a a lot of what I'll show you today is things that I've actually touched. Outside of work, I am a mom of a

toddler. I'm a member of a parent cooperative. So, if you're into that, come and talk to me after the talk. Uh, I like hiking and weightlifting. So, that's me. Here is the team. They're also in the room. So, say hi. Um, these are Tumir, Yordan, and Basil. We work closely together and particularly on Spring Data Readies. Uh, they're all ecosystems engineers. So what we do as ecosystems

engineers, we build the integrations between radius and the actual frameworks that people usually use. So spring data radius is one of them but not limited to that. So if you reach radius through any higher level framework, you've probably touched something that we have worked on. The contributions I will show you today came out of this team. First a short agenda. So we'll start uh with what spring

data radius because there are people in the room that haven't used it and where it sits between radius and spring. Then the heart of the talk is uh we are going to build a system together to hit real problems and to tackle them with the new APIs that we have contributed and provided and then we are going to end with a short summary and recap. Let's get

into it. So what is spring data radius? Um it's part of the spring spring data family. So it has the same shape as spring data JPA or spring data MongoDB. So if you've used any of those, this should feel familiar. Um can you tell me did anyone here use uh let's say the radius template or the radius hash notation? Maybe [laughter] not only the colleagues. Okay, that's

great. uh so I guess you will learn something new today then um and most of the teams use radius for caching and in generally I would say even key value caching in particular so that's usually as far as it goes and the rest of the talk is about everything beyond just the key value as I said radius has grown far um far apart far past simple key

value store and spring data radius is now bringing ing that all of those features into Spring natively. So I was thinking how do I show you that cuz I didn't want this to be a mere change log. So this method there those arguments here because it gets boring pretty fast. So instead I decided let's build something together a system with actual problems that we solve later using

spring data ready. And I thought why not build it for a conference. So this conference we're building J Prime. Uh again a disclaimer I want to flag that I made this presentation a few weeks ago and I didn't know about the hackathon that is going to be doing exactly this. But if you haven't added Radius to your hackathon app, you can get some inspiration right now and

maybe add it for the in the next I don't know 35 minutes until it ends. So you have some time. Um let's see what the platform looks like. So here on the left you can see the features. It's a sample of features um that such a platform could provide. Um and each feature looks simple enough but all of them as usually hide technical problems and those problems

we are going to solve together with radius and spring data radius. On the right you see a very simple architecture deliberately very simple. You just have a client layer. Then you have the service layer obviously using spring and spring data radius and then at the bottom you can see radius as a data store. Um you can see that there is something big is missing here and it's

the durable storage but we are going to focus in this talk on radius as the hot path. So the thing that you want to be fast, atomic, disposable. And this is my last setup slide before we go into actual use cases. So you can use this as a map. I just shown the different struct data structures we are going to touch. Uh because we have five problems

that we are going to uh go through and each of them uses its own data structure like hashes, streams, uh then string, JSON and search. We'll spend most of our time on the first structure with the hashes and the holds and the rest I will bring up once we reach to them. So our first story, our first use case is a workshop reservation system. We'll build the

reservation flow for a J prime workshop. How it used to be done before our contributions and how it can be done now. Picture this. You're at J Prime as you are browsing the agenda and you spot a workshop that looks pretty great say implementing agentic rack with using spring AI by your it's a small room you have 30 seats and the workshop is clearly popular um and

full transparency this workshop is actually running right now in the incubate incubator so you almost picked that up I appreciate you being here for for this talk so you tap reserve and [snorts] behind the scenes that creates a hold. This hold is something like a soft reservation which gives you about five minutes to complete a form. So during those five minutes um you write your expectations for

the workshop. You can write your years of experience with the technology at least five years obviously with aentic rack. Um then if you walk away expires on its own. So the seed goes back to the pool. But if you confirm the hold now becomes an actual real reservation. What also we what we would also like to have is to be able to understand how many holds exist

for a specific workshop. So let's build it together. We have three operations and one passive behavior from radius. So the three things we do explicitly. They are place a hold. That's the moment when a user clicks reserve. Um extend the hold while the user is still filling the form and consume it when they click the confirm button. And that atomically uh turns the hold into an actual

reservation. The passive behavior is the automatic expiry. So if the attendee walks away uh closes the tab or whatever the hold just disappears on its own. We do not want to do any manual bookkeeping on our site. We want radius to handle this for us. So those are the requirements. Now let's see what we built. Um the first instinct for maybe most of you that have used

radius before is to give every hold its own key. So we have the workshop the attendee um and the value is a token. Now using this data structure we get the ability to use um to give each single value a time to live a TTL and that part is lovely. Radius expires them on its own. It doesn't require any work from the application. The trouble is that

nothing connects those keys together. So they live in their they their own keys nothing typing them back to the workshop. So the holds for for example the question give me the holds for workshop 67 isn't something that you can easily ask and the only way to gather them is to scan by a specific pattern which is exa exactly what you don't want to do on a busy

server. So with this scenario we get an expiry for free but the holds actually do not belong together. Now you can see the code that achieves this data structure. Uh we have three methods. We have place a hold that is a uh that adds a key to um that adds a single key and gives it an expiry. Then we have extend a hold that pushes the time

to live. And we have a consume hold that reads the token and deletes the key and removes it when it turns into a real reservation. So three small methods, no manual bookkeeping for the expiry. Each key looks after its own lifetime. This is the one we'd reach for first, I would say. But it only breaks down the moment someone asks, "Show me every hold in this workshop."

there's nothing tying those keys together apart from the naming convention and the key space scan. Then a second approach um is to put everything under a single hash. [sighs] So every hold becomes every hold of a workshop becomes a field in this hash. So we have one field per attendee in this case Alice, Bob and Charlie. Now grouping is obviously solved. You can easily see the group.

They all go together. It's there, readable, easy to retrieve within a single call. But now you've lost something that we had in the previous version and that's the uh automatic expiry per field. Uh radius hash before version 8 I believe didn't or maybe 7 7.4 for uh didn't have automatic expiry per field and it could only expire the the whole hash. So in that case if if

Alice and Bob have different schedule technically for expiry we wouldn't be able to achieve it easily. So here is the code that um can achieve this data structure and you can see that you pay for the missing expiry with much more lines of code. For example, one idea is to perform manual bookkeeping by using an additional data structure. Let's say a set where you store the expiry

for every field that you have in the hash. Then you need to take care of that to clean it once it expires. You could end up in an invalid state like having a field that actually expired but is still in the set. So it becomes much much more complex. You can see placehold method writes to two data structures to the hash and then to the additional one.

Uh extend hold needs to do some additional checking uh if the if the expiry is already in the set or not etc. And consume hold also touches uh to two data structures. uh and that's what we lose basically by gaining the grouping the automatic grouping we lose the automatic expiry and you pay for it in every method. So here is the bind between the two. You can

see in the first case with the separate keys you had expiry for free but no grouping. Then in the second key in the second case you had one hash. So the grouping is there but an expiry that you need to take care of. So some manual bookkeeping whichever you pick you always gave something up. So either the time to live or the grouping you never got both.

But with our new contributions uh that the team has pushed to spring data, you can see now here the same hash one key the key holds uh every record Uh so very easy grouping. You can easily ask the question how many holds we have for workshop 67. But look at the fields now. Each of them has a separate expiry with a separate time to live and radius

actually expires them for us. So we do not have to do anything no bookkeeping. So that's the combination of the two previous approaches that we couldn't have the grouping and the perfield expiry together. So this is the data structure that we wanted to achieve for our requirements and we can finally have it in spring data ready API and the API to build it is relatively small of

course it's Java code so don't expect too much so here you can see we have three new methods the put and expire uh that allows you to place a hold in the put and expire you you add to the you add an expiration to the key and you do that atomically in a one single call. Then in the extend call, you just use the expire method which

adds more time to that particular attendee in the hash. And the consume hold now uses the new method get and delete uh which again in atomical way with a one single trip can get the attendees token and remove the record after it has turned into a real reservation. So you have no manual bookkeeping. Radius's um radius itself enforces the expiry for each field independently. And this API

is now already available in To summarize this, before we had a time to live without grouping or we had grouping with manual bookkeeping. We never had both. And now with the new API we have one grouped hash that allows per field time to live which is enforced by radius itself. So this is just one example of spring data radius beyond key value. Now let's look at another

example. The second story is a live speaker Q&A. And here's the natural data structure for for this use case is streams. And let's see our two recent contributions that make working with streams much easier than before. So this kind of problem is a bit different than the previous one where we had one attendee with a specific expiry and this time we have stream of events. So picture

a room like this. Uh during a talk questions are coming are coming in continuously from all over the audience. Um one after another while the even while the speaker is is still talking. The moderators work through work through them in order. So they answer some life. Uh they skip the ones that think they don't fit. Uh and once a question has been handled it shouldn't show up

again. It should be removed. And when the talk is over, the feed should be cleaned. So let's see how we build this with through operations. Again, we have three operations and a passive behavior, but this time it's streamshaped. The three we handle ourselves. So we have append a question as the question arise from the audience and is added to the stream. Then we have consume and acknowledge

when a moderator answers a specific question. And then we have clean up uh what's left when the talk has already finished. And like last time we have one passive be behavior where uh which is the trimming and we want to keep the stream capped to a certain bound uh without any manual bookkeeping. So if you have two too many questions coming in let's say 2,000 we want

to cap them to a thousand maximum. Uh we have one stream per conference. Every question for the event lands in the same place. Uh moderators read through it. So ready track which t which question has gone to which moderator. Uh this is the code that used to achieve this before. So again we have three methods. We have the append question which as you can see performs two

operations. So it adds um the question to the stream and then it also needs to trim it through another additional operation. Um then we have the answer question which again does it in two separate calls to the server. So first we acknowledge the message and then we actually remove it from the stream and the cleaning meant going through the leftover questions and deleting them one at a

time. every operation was really actually two at least two calls to the server with the application application layer doing the stitching and now after our contributions you see the same three methods uh but now we can achieve uh every action in a single call. So appending a question uh here actually carries the cap together with it because there are new parameters added to the method. So the

trimming is added there and radius actually trims as it writes the questions. Then answering becomes acknowledge and delete method. Um and that's again one call that marks the question as done and removes it um removes it from the stream and cleaning up becomes delete with options. So again we have one call that removes the leftovers and tidies uh the consumer group references along with them. So three

operations but one call each and the orchestration now is moved from the application layer to the server. And again to repeat the same pattern as last time before every stream operation was more than just a single call at least two with the application doing the orchestration and now after our contributions radius handles it. So we have one call per intent. That was our second story. So let's

go to the third one. The first story is going live. So here we have a single contribution that um is a very let's see the scenario. It's a clean scenario but it's a small change that actually takes out surprising amount of code. So let let me set it up so that you understand. So it's 320. uh your talk is about to start and you tap a button

go live and a >> [snorts] >> uh actions happen behind the scene. So the Q&A feed opens then the talk goes live the recording starts and you are not the only one as a speaker that can perform this. It can [clears throat] be performed or also by uh let's say the organ or organizers dashboard or the moderators depending on the role and two people could possibly press

the same button at the same time that could happen and there could be some damage let's say double recording or a talk that has actually ended but is now reopened. So the transitions have to be safe and how do we achieve that with a small state machine. So each transition is allowed from exactly one starting state and nowhere else. That's what rules out the failures from a

moment ago. So you can't really go live twice and start the recording twice and you can't end the talk that never started. The rules themselves are simple. And the hard part here is um if you have two requests that land at the same time. Let's see how we deal with that. So before um there could be a naive approach obviously. So you could just read the state,

check that it's scheduled and then move it to life. But in case two clients do it at the same time, they could both read the scheduled state and um that would mean that the second client could also move the state again. So moving it twice and we don't want that. And the answer before used to be a L script. So the script reads the value, compares it

and writes the new one only if the state matches the desired state. And all of this was an atomic operation and it works. Actually people use it a lot. Uh the race condition here is gone. But there is a cost to this. So you can see Lisa script in the Java code. um not proper ID support, type checking, um like a second little language that you have

in your code. And as my colleague pointed out, even if you upload your law script to the radius server as you can do and just call it by name, you still if you have a lot of transitions, you might end up with plenty of law scripts that you need to support. And let's see how this can be done now after our contributions to spring data So now

you can use the natural set method that the people that use the spring data radius have used it for sure that sets key value but you can uh set a specific condition to it. So the condition says set this value but only if the current one is let's say in the first example scheduled. So set this talk to life only if it's previous status was scheduled. And

this is exactly the same logic as in the Lisa script but now it's a type Java call and a single operation. You don't have any script that you need to support. And the check still happens inside radius. So radius does it for you not on the application layer. It's an atomic single command. If two clients go live what will happen? uh both will call this method let's

say go live uh the first one will succeed and it will flip flip the state to life but the second one just won't succeed and won't move the state again and that's again atomically without louisis scripts and to repeat again what used to to take orchestration in our code or maybe luis scripts radius now can does can do directly for us in a And the spring API

reflects that you saw the you have one method per intent. So the previous contribution contributions that I showed you are already merged into spring data radius. Some are released, some are in a release candidate. Now we are going to look into what's still in progress. So what is coming next? And the next two features I'm going to show you, they, as I said, they're not shipped, but

they're in active development. I believe they're worth for you to know because they're interesting and they're where the things are heading for the ecosystem of Spring Data Reads. So the data J Prime conference system can fit pretty easily in a JSON format. So take one attendee profile for example. Attendee profile has a name, has a company. So that's simple. But there is also, let's say, a list

of talks that they have already bookmarked and maybe some notification settings like subscribe to the newsletter of J Prime for example. So that means a list inside uh you have a list inside the record, you have objects inside the record. So a complex data structure and it's not naturally flat. It's a small tree. But currently, Radius forces you to kind of flat it, flatten it. Um, and

if you put it in a plain key value, you have to, as I said, flatten it and then stitch it back together yourself. But as JSON, uh, that's the natural shape that such data would fit in. And here's the thing about data like this. Uh, data like this usually changes in small local places. Uh, so you don't rewrite the whole profile of an attendee. you actually flip

one togo. Let's say the newsletter subscription, you subscribe or unsubscribe. Maybe you fix a typo in the company name, stuff like this. Um, so you touch a very small part of the much bigger record and you never kind of replace the whole thing altogether. And right now with the previous approach, you actually had to do that to replace the whole thing. So today without a native support

um the whole structure that goes into radius is a single serialized string which is fine but until you need to change a small part of it. Let's say you want to add a comment to to a talk there's no way to reach in and touch just that single comment array. You pull the entire value back to your application. Then you d serialize it into objects. find the

comments then add new one then again serialize and then write it back the whole thing. So replace as I said the full value travels um to your app and back. So you have two round trips on the wire to append just a single line and that's not atomic. So if two people comment at the same time they both start they both could start from the same copy

of the data and then the second right would win. So one comment would be lost. And now with as I told you with what is coming to the to the framework. Uh you can see here we have the same JSON and now we have only one string appended at the bottom and we are using native JSON operations from radius instead of manual string manipulation. Uh the other

fields are left untouched. We just add the one new entry particularly to that array. We do it in a single operation. There are no round trips and it's atomic. If two people comment at the same time at the same instant um both codes would succeed in order and nothing would be lost. So we are the team is working to expose to expose this API through spring data

radius because it's already available in radius and that's the JSON support that I wanted to share with you. Now we have kind of the same idea with the search. Let's see this look at this question. So show me all nonI talks happening after lunch with at least one open seat. So that's a real question you might want to ask in the agenda for for such a conference

like J Prime and it sounds like a oneliner. Yes, the sort of thing that the system should easily answer but with plain key value it's actually not that simple. The trouble with key value is that it's good in one thing and that's give me the value for this key. it has no idea how to ask which dogs fall after lunch or which ones still have an open

seat etc. Each of those um is a different type of matching and key value just doesn't support it. So a question like this turns into a real pile of work that has to be put put together by hand. So let's see it now. Um currently in spring data radius you could you could you are able to do this using secondary indexes but let's see how are they

implemented. So the current implementation is uh using an auxiliary sets which act as the secondary index and you have one such set per filter. So we have one set for the tracks let's say we have one set for the time whether it's morning afternoon etc. We have additional set for the seats available. So all data struct three already three data structures that needs to be supported which

which is a lot of manual bookkeeping again and currently if you use the index annotation by spring data radius the framework will take care of that for you so you want to you you won't need to do that yourself um and it's a real convenience yes but it's still more data structures additional ones and still they do not provide the full functionality because in this case you

could do um exact matches particularly for the tracks only. So no partial matches uh relevant sorting etc. So the you could answer the question track uh track equals non AI but searching in the title let's say for non AI is not trivial now and with our upcoming work the contributions that we are preparing for string data radius uh we can use the same hash for the talks

as I showed you previously but instead of using auxiliary sets now we have a single will index over all the talks and you tell radius which fields to index and how. For example, the title might be full text to provide you full text search and partial matches. The track might be of type tag where you can um do you can perform exact tag matching. Then time could

be numeric or tag again whatever you choose. And it even reaches further than that the the whole search uh because it adds geo for location search and it also adds vector search for the llm style similarity work and radius keeps the index updated as you write. So if you add additional records uh to the talk hash the index will be updated for you. So no manual work

from from the developer side, no sets to maintain etc. Much much more simple. And now you can answer the question how many nonAI talks are happening after lunch with available seats with just a single query. Spring data ready work to expose this API is currently in progress. Now before we wrap up uh there are a few more recent contributions additions that I think it's worth knowing about

but they did not deserve such a deep dive as the previous ones. So we have um added the expanded bit map operations that bring along set algebra as first class operations. Uh things that before you needed to do with two calls to radius let's say the bit operation and or the bit operation or. Now you have you have them combined and the rest of them you see

on the screen. We have also added expiration based set API. So that bundles together three separate operations into one call. So you could set a value at the same time added a time to live and at the same time uh set a condition um whether to set the value or not. So those are now packed Uh we also have added the digest based comparison. It's very useful

again for the compare and swap um operation because if you have a big if you have a big payload payload sorry um then it could be much more efficient to just compare it to a small digest and not send the whole payload to the server and process it back again. We have also added the counterpart of compare and compare and swap to which is compare and delete

which does the same thing but for the delete operation. So delete this value only if it meets a certain condition. So none of this earned a story of their own but again uh I wanted to show something general that we are moving more work to the server and removing it from the application code. So prov using spring data radius in this case. Now to wrap up that's

everything that I wanted to show you. So let's recap. Um at the very beginning I said spring data radius has been moving beyond key value and now you've seen what I actually mean with that. Uh look at what every one of these stories has in common. So in each one of them we were stitching together um things in our own code. Uh whether it would be extra

data structure to maintain, whether it would be a Luis script written, whether it would be extra round trips to make to the server, it doesn't matter. Now we actually turned those into single operations that Reddis can handle for That is the real threat running through through all of them. Real connection. So the workshop reservations, the live Q&A, going live, three real problems and in everyone uh the

work was moved um from the application to the database and the two that are still in progress, the JSON and search are heading in the exact same direction, just simplifying application code and allowing the server to do what it can do best. [snorts] So this is what beyond key value looks like. If you take away anything from this, take away this. So, first go and try the

APIs. Um, as I mentioned, hashfield expirations, they are already in spring data radius 4.0 and it's stable and the com um conditional sets, conditional deletes, the digest and the other operations that I showed there in 4.1 which is in release candidate right now. So you can run them against your own data. Uh see what fits, where it fits, how it fits, what doesn't fit, uh how the

API literally feels. Then um this is the important part that matters to us as a community. Tell us how it went. Tell us what worked, what didn't work, uh what was missing, what you would maybe like to see in the future. Just reach out. uh because spring data rate is is built around its users like there is a whole ecosystem as you know for spring. So feedback

is how the system knows where to move actually in which d direction it should take. So that was all for me. Thank you very much. [applause] If you have questions, feel free to ask now or later. Yes. >> Mhm. >> Well, as I showed you the example, yes, I can repeat. He he was asking I mentioned atomic operations. How do we achieve those? So, as I showed

you before, So the spring data code that I showed you, it maps to specific commands in the register server. So the example let's say with the hashes or with set or with whatever you want. U before they were two separate operations, two separate commands that were actually executed on the server and now the server has provided one command that can do the same action. So spring data

radius is just the abstraction layer and now we have added that API to spring data radius. So that's how we achieve the atomicity. Like a like a Yeah, exactly. >> But yes, invert inverted. Yeah. >> the question was what what was exactly the index? Can you do full text search with it? Right, there are spec there is a schema that you define for the index. So for

each field that you have you can define what the type of that field is whether it's a text field where you can do a full text search or if it's numeric and do some ranges with it or as I said even vector search for similarity searches so different schemas yeah yeah but not yeah but as I said not yet in the spring data radius API but it

will be there you Just follow the repository and you'll see when it's >> Any other questions? I I don't see from the lights to be honest, so just shout. Okay. Thank you again. >> [applause]

From event

jPrime 2026

03 Jun 2026 – 04 Jun 2026

All event videos
Back to Watch