{"id":20828782,"url":"https://github.com/perty/sse","last_synced_at":"2025-07-23T08:05:10.093Z","repository":{"id":137744637,"uuid":"345822593","full_name":"perty/sse","owner":"perty","description":"Experiments with server side events and Spring Boot","archived":false,"fork":false,"pushed_at":"2021-03-10T22:05:16.000Z","size":12,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-07-23T08:04:11.992Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/perty.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-08T23:20:09.000Z","updated_at":"2021-03-25T20:58:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"e2b3a520-a541-475b-be53-878b5a865768","html_url":"https://github.com/perty/sse","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/perty/sse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perty%2Fsse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perty%2Fsse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perty%2Fsse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perty%2Fsse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/perty","download_url":"https://codeload.github.com/perty/sse/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/perty%2Fsse/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266640831,"owners_count":23960809,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-17T23:18:26.485Z","updated_at":"2025-07-23T08:05:10.027Z","avatar_url":"https://github.com/perty.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Experimenting with reactive and server sent events\n\nHere is a couple of examples or rather, experiments around reactive programming.\n\nI have used Spring Boot Webflux and R2DBC driver with a Postgresql database.\n\n## Observations and learnings\n\n### Server sent event\n\nWhen setting up a listener for event streams, Chrome seems to be polling. That was not my expectation, I expected it to be a server push technology.\n\nIs it a combination of the server implementation and the browser doing some kind of fall back? \n\nThe JavaScript for to register an ``EventSource``:\n```\nconst evtSource = new EventSource(\"http://localhost:8080/events\");\nevtSource.onmessage = function (event) {\n    const newElement = document.createElement(\"li\");\n    const eventList = document.getElementById(\"list\");\n\n    newElement.innerHTML = \"Event data  : \" + event.data;\n    eventList.appendChild(newElement);\n}\n```\n\nThe server side is using a reactive construction:\n```\n@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)\npublic Flux\u003cServerSentEvent\u003cString\u003e\u003e getMessages(@RequestHeader HttpHeaders headers) {\n    List\u003cString\u003e lastIds = headers.get(\"Last-Event-ID\");\n    String lastId = lastIds == null ? \"0\" : lastIds.get(0);\n    return messageService.getMessages(lastId);\n}\n```\nBy using the ``Last-Event-ID`` header attribute the last id the client can communicate which the last id it had received. Without it, all messages would be received again. \n\nWikipedia [https://en.wikipedia.org/wiki/Server-sent_events](https://en.wikipedia.org/wiki/Server-sent_events)\n\nMozilla [https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)\n\n### Reactive database\n\nIt is not just a replacement of the driver, there are several other things that are different, compared to using Spring JPA.\n\n#### Entity annotation\nThe class to represents persisted data [MessageEntity](src/main/java/se/artcomputer/edu/sse/MessageEntity.java), do **not** have an `@Entity` annotation.\n\n#### Generating primary id\nThe `@GeneratedValue` is not available, so I relied on Postgresql to generate a primary key.\n\n#### Implement Persistable\nI am not sure about this, but it seems to be important that the entity class ``implements Persistable\u003cLong\u003e``, which simply tells whether to insert or update.\n\n#### Repository interface\nInstead of using `JPARepository`, you need to use `ReactiveCrudRepository` which returns `Flux` objects. But the experience is similar in other aspects.\n\n#### Flyway\nFlyway does not support R2DBC (there is an issue about it) so I needed to add a section in the application configuration where Flyway gets a JDBC url: \n  ```\n    flyway:\n        url: jdbc:postgresql://localhost:5432/sse\n        user: postgres\n        password: mysecret\n  ```\n  It is enough, though, unlike other sources claim.\n  \n#### Creating many entities\nWhen creating 1000 entities using a reactive construction, the order of the creation is not sequential, which I think is cool. \n\nHere is the construction for creating entities and returning the data transfer object `Message`:\n```\npublic Flux\u003cMessage\u003e generate() {\n    return Flux.range(0, 1000).flatMap(this::create);\n}\n\nprivate Mono\u003cMessage\u003e create(Integer n) {\n    MessageEntity entity = new MessageEntity();\n    entity.setMessage(\"This is generated \" + n);\n    return messageRepository.save(entity).map(this::toDto);\n}\n```\nAs you can see, the messages are numbered and as said, the order of them are not they same in the database. This is evident from the primary key that is generated by Postgresql. Eg:\n\n|id |message\n|---|-------\n|9 |This is generated 7\n|10|This is generated 12\n|11|This is generated 13\n|12|This is generated 14\n|13|This is generated 9\n|14|This is generated 10\nCool, right?\n\n#### Reading many entities\n\nI would like to see the entities come trickling when querying for 1000 entities at once but they came all in one bunch. So I need to study this more.\n\nOk, so this put me on the right track somehow [Full Reactive Stack with Spring Boot, WebFlux and MongoDB](https://thepracticaldeveloper.com/full-reactive-stack-2-backend-webflux/).\n\nThe `delayElements` of `Flux` works in combination with server-sent events. It comes together here! By not stating the media type at the endpoint, I can use curl and accept header to get them trickling, like so:\n```\n curl -H \"Accept: text/event-stream\" http://localhost:8080/messages/slow\n```\nIt keeps asking for more so when 1000 entities have been sent, it starts over. Apparently what a client does when using this protocol so it is not just the browser.\n\nOk, this start-over business has to do with `delayElements` somehow. If I remove it, curl will terminate after all elements. Not intuitive to me. \n\nThere might be some timing issues. Sometimes curl hangs, sometimes it terminates and sometimes it keeps asking for more. \n\nWith a `log()` inserted I can see what is going on. \n\nSomething, perhaps Netty, is doing `request(24)` when the server responding quickly. It ends with a `onComplete()`.\n\nWhen slow, there are only `onNext(MessageEntity{id=919, message='This is generated 919'})` and they keep coming.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperty%2Fsse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperty%2Fsse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperty%2Fsse/lists"}