{"id":23082448,"url":"https://github.com/fencyio/fency","last_synced_at":"2026-03-13T19:06:23.990Z","repository":{"id":57732029,"uuid":"169045197","full_name":"fencyio/fency","owner":"fencyio","description":"Provides an idempotency barrier for RabbitMQ consumers.","archived":false,"fork":false,"pushed_at":"2019-11-27T11:37:38.000Z","size":170,"stargazers_count":21,"open_issues_count":1,"forks_count":9,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-30T16:29:24.121Z","etag":null,"topics":["idempotency","rabbitmq","redis","spring-amqp","spring-boot","spring-data-redis","testcontainers"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fencyio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-04T08:02:17.000Z","updated_at":"2025-08-13T11:19:45.000Z","dependencies_parsed_at":"2022-09-03T23:22:23.349Z","dependency_job_id":null,"html_url":"https://github.com/fencyio/fency","commit_stats":null,"previous_names":["ask4gilles/fency"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/fencyio/fency","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fencyio%2Ffency","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fencyio%2Ffency/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fencyio%2Ffency/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fencyio%2Ffency/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fencyio","download_url":"https://codeload.github.com/fencyio/fency/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fencyio%2Ffency/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30472989,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-13T17:15:31.527Z","status":"ssl_error","status_checked_at":"2026-03-13T17:15:22.394Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["idempotency","rabbitmq","redis","spring-amqp","spring-boot","spring-data-redis","testcontainers"],"created_at":"2024-12-16T14:52:45.974Z","updated_at":"2026-03-13T19:06:23.967Z","avatar_url":"https://github.com/fencyio.png","language":"Java","readme":"[![Build Status][ci-img]][ci]\n[![codecov](https://codecov.io/gh/fencyio/fency/branch/master/graph/badge.svg)](https://codecov.io/gh/fencyio/fency)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/7ee34d1388f549e1ad3298a967f388f0)](https://www.codacy.com/app/ask4gilles/fency?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=ask4gilles/fency\u0026amp;utm_campaign=Badge_Grade)\n[![Released Version][maven-img]][maven]\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n# Fency: an idempotency barrier for RabbitMQ consumers\n## Theoretical concept\nEven when a sender application sends a message only once,\nthe receiver application may receive the message more than once.\n\nThe term idempotent is used in mathematics to describe a function that produces the same result \nif it is applied to itself: f(x) = f(f(x)). \nIn Messaging this concepts translates into a message that has the same effect whether it is received \nonce or multiple times. \nThis means that a message can safely be resent without causing any problems even if the receiver receives \nduplicates of the same message.\n\nThe recipient can explicitly de-dupe messages by keeping track of messages that it already received. \nA unique message identifier simplifies this task and helps detect those cases where \ntwo legitimate messages with the same message content arrive.\n\nIn order to detect and eliminate duplicate messages based on the message identifier, \nthe message recipient has to keep a list of already received message identifiers.\n\n## Technical implementation\n\nIn order to store the processed message metadata, we have to be in a transactional context.\nIf something goes wrong, the transaction has to be roll backed.\n\n1.  The **MessageInterceptor** creates an **MessageContext** and stores it in a ThreadLocal\n\n2.  The **IdempotencyBarrier** is an aspect around the **@IdempotentConsumer** annotation. \nIt retrieves the MessageContext and checks if the message already exists. \nThe unique message key is composed by the messageId and the consumerQueueName.\n\nIf the message does not exist, the target method is invoked and the message metadata is stored in a datastore.\n\nIf the message already exists, an error message is logged and the target method is not invoked.\n\n## Usage\n\n* Maven\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.fency\u003c/groupId\u003e\n  \u003cartifactId\u003efency-spring-boot-starter-redis\u003c/artifactId\u003e\n  \u003cversion\u003e0.1.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n* Gradle\n```text\nimplementation 'io.fency:fency-spring-boot-starter-redis:+'\n```\n\nAnnotate your consumers with @IdempotentConsumer.\n\nSee sample app: [fency-spring-boot-sample-app](https://github.com/fencyio/fency/tree/master/fency-spring-boot-sample-app)\n\n## Note for spring integration users\nIf you are already using spring integration, have a look \n[here](https://docs.spring.io/spring-integration/docs/current/reference/html/#idempotent-receiver)\n\n## References\n* [Idempotent receiver](https://www.enterpriseintegrationpatterns.com/patterns/messaging/IdempotentReceiver.html)\n\n[ci-img]: https://api.travis-ci.com/fencyio/fency.svg?branch=master\n[ci]: https://travis-ci.com/fencyio/fency\n[maven-img]: https://img.shields.io/maven-central/v/io.fency/fency-core.svg\n[maven]: http://search.maven.org/#search%7Cga%7C1%7Cio.fency\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffencyio%2Ffency","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffencyio%2Ffency","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffencyio%2Ffency/lists"}