{"id":13481572,"url":"https://github.com/monix/shade","last_synced_at":"2025-05-05T15:31:44.265Z","repository":{"id":9120809,"uuid":"10906395","full_name":"monix/shade","owner":"monix","description":"Memcached client for Scala","archived":false,"fork":false,"pushed_at":"2021-03-18T23:25:57.000Z","size":285,"stargazers_count":106,"open_issues_count":13,"forks_count":19,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-05-22T01:00:23.767Z","etag":null,"topics":["memcached","monix","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/monix.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-06-24T11:30:05.000Z","updated_at":"2024-03-17T22:56:18.000Z","dependencies_parsed_at":"2022-07-09T23:00:33.989Z","dependency_job_id":null,"html_url":"https://github.com/monix/shade","commit_stats":null,"previous_names":["bionicspirit/shade"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monix%2Fshade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monix%2Fshade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monix%2Fshade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monix%2Fshade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/monix","download_url":"https://codeload.github.com/monix/shade/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224452805,"owners_count":17313668,"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","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":["memcached","monix","scala"],"created_at":"2024-07-31T17:00:52.941Z","updated_at":"2024-11-13T13:08:21.384Z","avatar_url":"https://github.com/monix.png","language":"Scala","readme":"# Shade - Memcached Client for Scala\n\n[![Build Status](https://travis-ci.org/monix/shade.svg?branch=master)](https://travis-ci.org/monix/shade)\n[![Join the chat at https://gitter.im/monix/shade](https://badges.gitter.im/monix/shade.svg)](https://gitter.im/monix/shade?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n## Overview\n\nShade is a Memcached client based on the de-facto Java library\n[SpyMemcached](https://code.google.com/p/spymemcached/).\n\nThe interface exposed is very Scala-ish, as you have a choice between\nmaking asynchronous calls, with results wrapped as Scala\n[Futures](http://docs.scala-lang.org/overviews/core/futures.html),\nor blocking calls. The performance is stellar as it benefits from the\n[optimizations that went into SpyMemcached](https://web.archive.org/web/20140914202520/https://code.google.com/p/spymemcached/wiki/Optimizations)\nover the years. Shade also fixes some problems with SpyMemcached's\narchitecture, choices that made sense in the context of Java, but\ndon't make so much sense in the context of Scala (TODO: add details).\n\nThe client is production quality.\nSupported for Scala versions: 2.10, 2.11 and 2.12.\n\n## Release Notes\n\n- [Version 1.10.x](release-notes/1.10.md)\n- [Version 1.9.x](release-notes/1.9.md)\n- [Version 1.8.x](release-notes/1.8.md)\n- [Version 1.7.x](release-notes/1.7.md)\n- [Version 1.6.0](release-notes/1.6.0.md)\n\n## Maintainers\n\nThese are the people maintaining this project that you can annoy:\n\n- Alex: @alexandru\n- Lloyd: @lloydmeta\n\n## Usage From SBT\n\n```scala\ndependencies += \"io.monix\" %% \"shade\" % \"1.10.0\"\n```\n\n### Initializing the Memcached Client\n\nTo initialize a Memcached client, you need a configuration object.\nCheckout the\n[Configuration](src/main/scala/shade/memcached/Configuration.scala)\ncase class.\n\n```scala\nimport shade.memcached._\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nval memcached =\n  Memcached(Configuration(\"127.0.0.1:11211\"))\n```\n\nAs you can see, you also need an\n[ExecutionContext](http://www.scala-lang.org/api/current/#scala.concurrent.ExecutionContext)\npassed explicitly. As an implementation detail, the execution context represents the\nthread-pool in which requests get processed.\n\n### Simple non-blocking requests\n\nUseful imports:\n\n```scala\nimport concurrent.duration._ // for specifying timeouts\nimport concurrent.Future\n```\n\nSetting a key:\n\n```scala\nval op: Future[Unit] = memcached.set(\"username\", \"Alex\", 1.minute)\n```\n\nAdding a key that will only set it if the key is missing (returns true\nif the key was added, or false if the key was already there):\n\n```scala\nval op: Future[Boolean] = memcached.add(\"username\", \"Alex\", 1.minute)\n```\n\nDeleting a key (returns true if a key was deleted, or false if the key\nwas missing):\n\n```scala\nval op: Future[Boolean] = memcached.delete(\"username\")\n```\n\nFetching a key:\n\n```scala\nval result: Future[Option[String]] = memcached.get[String](\"username\")\n```\n\nAs you can see, for fetching a key the `get()` method needs an\nexplicit type parameter, otherwise it doesn't know how to deserialize\nit. More on this below.\n\n### Blocking requests\n\nSometimes working with Futures is painful for quick hacks, therefore\n`add()`, `set()`, `delete()` and `get()` have blocking versions in the\nform of `awaitXXX()`:\n\n```scala\nmemcached.awaitGet(\"username\") match {\n  case Some(username) =\u003e println(s\"Hello, $username\")\n  case None =\u003e\n    memcached.awaitSet(\"username\", \"Alex\", 1.minute)\n}\n```\n\n### Compare-and-set\n\nSometimes you want to have some sort of synchronization for modifying\nvalues safely, like incrementing a counter. Memcached supports\n[Compare-And-Swap](http://en.wikipedia.org/wiki/Compare-and-swap)\natomic operations and so does this client.\n\n```scala\nval op: Future[Boolean] =\n  memcached.compareAndSet(\"username\", Some(\"Alex\"), \"Amalia\", 1.minute)\n```\n\nThis will return either true or false if the operation was a success\nor not. But working with `compareAndSet` is too low level, so the\nclient also provides these helpers:\n\n```scala\ndef incrementCounter: Future[Int] =\n  memcached.transformAndGet[Int](\"counter\", 1.minute) {\n    case Some(existing) =\u003e existing + 1\n    case None =\u003e 1\n  }\n```\n\nThe above returns the new, incremented value. In case you want the old\nvalue to be returned, do this:\n\n```scala\ndef incrementCounter: Future[Option[Int]] =\n  memcached.getAndTransform[Int](\"counter\", 1.minute) {\n    case Some(existing) =\u003e existing + 1\n    case None =\u003e 1\n  }\n```\n\n### Serializing/Deserializing\n\nStoring values in Memcached and retrieving values involves serializing\nand deserializing those values into bytes. Methods such as `get()`,\n`set()`, `add()` take an implicit parameter of type `Codec[T]` which\nis a type-class that specifies how to serialize and deserialize values\nof type `T`.\n\nBy default, Shade provides default implementations of `Codec[T]` for\nprimitives, such as Strings and numbers. Checkout\n[Codec.scala](src/main/scala/shade/memcached/Codec.scala) to see those\ndefaults.\n\nFor more complex types, a default implementation based on Java's\n[ObjectOutputStream](http://docs.oracle.com/javase/7/docs/api/java/io/ObjectOutputStream.html)\nand\n[ObjectInputStream](http://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html)\nexist (also in Codec.scala).\n\nHowever, because serializing/deserializing values like this is\nproblematic (you can end up with lots of errors related to the\nClassLoader used), this codec is available as part of the\n`MemcachedCodecs` trait (also in\n[Codec.scala](src/main/scala/shade/memcached/Codec.scala)) and it\neither needs to be imported or mixed-in.\n\nThe import works like so:\n\n```scala\nimport shade.memcached.MemcachedCodecs._\n```\n\nBut this can land you in trouble because of the ClassLoader. For\nexample in a Play 2.x application, in development mode the code is\nrecompiled when changes happen and the whole environment gets\nrestarted. If you do a plain import, you'll get `ClassCastException`\nor other weird errors. You can solve this by mixing-in\n`MemcachedCodecs` in whatever trait, class or object you want to do\nrequests, as in:\n\n```scala\ncase class User(id: Int, name: String, age: Int)\n\ntrait HelloController extends Controller with MemcachedCodecs {\n   def memcached: Memcached // to be injected\n\n   // a Play 2.2 standard controller action\n   def userInfo(id: Int) = Action.async {\n     for (user \u003c-  memcached.get[User](\"user-\" + id)) yield\n       Ok(views.showUserDetails(user))\n   }\n\n   // ...\n}\n```\n\nOr, in case you want to optimize serialization/deserialization, you\ncan always implement your own `Codec[T]`, like:\n\n```scala\n// hackish example\nimplicit object UserCodec extends Codec[User] {\n  def serialize(user: User): Array[Byte] =\n    s\"${user.id}|${user.name}|${user.age}\".getBytes(\"utf-8\")\n\n  def deserialize(data: Array[Byte]): User = {\n    val str = new String(data, \"utf-8\")\n    val Array(id, name, age) = str.split(\"|\")\n    User(id.toInt, name, age.toInt)\n  }\n}\n```\n","funding_links":[],"categories":["Database","Table of Contents"],"sub_categories":["Database"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonix%2Fshade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmonix%2Fshade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonix%2Fshade/lists"}