{"id":19939406,"url":"https://github.com/yito88/scarab","last_synced_at":"2026-05-16T17:05:48.102Z","repository":{"id":62434573,"uuid":"160763096","full_name":"yito88/scarab","owner":"yito88","description":"Clojure wrapper of ScalarDB","archived":false,"fork":false,"pushed_at":"2026-02-27T22:08:46.000Z","size":45,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-30T19:07:42.965Z","etag":null,"topics":["database","scalardb","transaction"],"latest_commit_sha":null,"homepage":"","language":"Clojure","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/yito88.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APL-2.0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-07T03:01:38.000Z","updated_at":"2026-02-27T22:08:49.000Z","dependencies_parsed_at":"2022-11-01T21:02:24.028Z","dependency_job_id":null,"html_url":"https://github.com/yito88/scarab","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/yito88/scarab","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yito88%2Fscarab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yito88%2Fscarab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yito88%2Fscarab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yito88%2Fscarab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yito88","download_url":"https://codeload.github.com/yito88/scarab/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yito88%2Fscarab/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33111497,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["database","scalardb","transaction"],"created_at":"2024-11-12T23:46:15.255Z","updated_at":"2026-05-16T17:05:48.088Z","avatar_url":"https://github.com/yito88.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Clojars Project](https://img.shields.io/clojars/v/scarab.svg)](https://clojars.org/scarab)\n[![GitHub Actions](https://github.com/yito88/scarab/workflows/All%20tests/badge.svg)](https://github.com/yito88/scarab/actions)\n\n# Scarab\n\nA Clojure wrapper of [Scalar DB](https://github.com/scalar-labs/scalardb)\n\n## Install\n\nAdd the following dependency to your `project.clj` file:\n```clojure\n[scarab \"1.1.0\"]\n```\n\n## Usage\n\n- You need to create a namespace and a table for Scalar DB before use\n  - You can create them easily with [Schema Tools](https://github.com/scalar-labs/scalardb/tree/master/tools/schema)\n\n### Storage (non transactional operation)\n\n```clojure\n(require '[scarab.core :as c])\n(require '[scarab.storage :as st])\n\n(let [config {:nodes [\"192.168.1.30\" \"192.168.1.31\" \"192.168.1.32\"]\n              :username \"cassandra\"\n              :password \"cassandra\"}\n      storage (st/prepare-storage config)\n      partition-keys {:id [1 :int] :name [\"XXX\" :text]}\n      clustering-keys {:age [11 :int]}\n      values  {:val1 [111 int] :val2 [\"value 2\" :text]}]\n\n      (st/put storage {:namespace \"test\"\n                       :table \"testtbl\"\n                       :pk partition-keys\n                       :ck clustering-keys\n                       :values values})\n\n      (st/select storage {:namespace \"test\"\n                          :table \"testtbl\"\n                          :pk partition-keys\n                          :ck clustering-keys}))\n```\n\n- First, you need to get a storage service with properties\n  - If you give an empty map as properties, it will be connected to a local server.\n  ```clojure\n  (st/prepare-storage {})\n  ```\n\n- You can operate records by `select`, `put` and `delete` with storage service\n  ```clojure\n  (st/select storage {:namespace \"test\"\n                      :table \"testtbl\"\n                      :pk partition-keys\n                      :ck clustering-keys} ;; :ck is optional\n  (st/put storage {:namespace \"test\"\n                   :table \"testtbl\"\n                   :pk partition-keys\n                   :ck clustering-keys ;; :ck is optional\n                   :values values})\n  (st/delete storage {:namespace \"test\"\n                      :table \"testtbl\"\n                      :pk partition-keys\n                      :ck clustering-keys}) ;; :ck is optional\n  ```\n\n- Columns are represented as a map\n  ```clojure\n    {:column-name1 {val1 :value-type1}\n     :column-name2 {val2 :value-type2}}\n  ```\n  - `:value-type` supports `:bigint`, `:blob`, `:boolean`, `:double`, `:float`, `int`, and `text`\n\n- Partition keys and clustering keys are represented as a map\n  ```clojure\n    {:key-name1 [\"partition key 1\" :text]\n     :key-name2 [22 :int]}\n  ```\n\n- You can specify a consistency level to `select`/`scan`/`put`/`delete` a record\n  ```clojure\n  (st/select storage {:namespace \"test\"\n                      :table \"testtbl\"\n                      :pk partition-keys\n                      :cl :eventual})\n  (st/select storage {:namespace \"test\"\n                      :table \"testtbl\"\n                      :pk partition-keys\n                      :cl :eventual})\n  (st/put storage {:namespace \"test\"\n                   :table \"testtbl\"\n                   :pk partition-keys\n                   :values values\n                   :cl :sequential})\n  (st/delete storage {:namespace \"test\"\n                      :table \"testtbl\"\n                      :pk partition-keys\n                      :cl :sequential})\n  ```\n  - You can see the detail of consistency level in [Scalar DB](https://scalar-labs.github.io/scalardb/javadoc/com/scalar/database/api/Consistency.html)\n\n#### Scan options\n```clojure\n(st/scan storage {:namespace \"test\"\n                  :table \"testtbl\"\n                  :pk partition-keys\n                  :start-ck {:ck1 [100 :int]}\n                  :inclusive-start? true\n                  :end-ck {:ck1 [200 :int]}\n                  :inclusive-end? false\n                  :ordering {:ck2 :desc}\n                  :limit 3})\n```\n- `:start-ck`: This option specifies the starting point of the scan. You can specify a clustering key for the point.\n- `:inclusive-start?`: If true, the starting point is included.\n- `:end-ck`: This option specifies the end point of the scan. You can specify a clustering key for the point.\n- `:inclusive-end?`: If true, the end point is included.\n- `:ordering`: This option specifies the ordering (`:asc` or `:desc`) of the specified clustering key.\n- `:limit`: The limit of the number of records. It should be positive.\n\n#### Conditional mutations\n- Insert a new record only if a record with the specified keys doesn't exist\n```clojure\n(st/put storage {:namespace \"test\"\n                 :table \"testtbl\"\n                 :pk partition-keys\n                 :ck clustering-keys\n                 :values values\n                 :if-exists false})\n```\n\n- Update a record only if the record with the specified keys exists\n```clojure\n(st/put storage {:namespace \"test\"\n                 :table \"testtbl\"\n                 :pk partition-keys\n                 :ck clustering-keys\n                 :values new-values\n                 :if-exists true})\n```\n\n- Update a record only if the record with the specified keys satisfies the given conditions\n```clojure\n(st/put storage {:namespace \"test\"\n                 :table \"testtbl\"\n                 :pk partition-keys\n                 :ck clustering-keys\n                 :values new-values\n                 :condition [[:eq :val1 [100 :int]]\n                             [:ne :val2 [\"immutable\" :text]]]})\n```\n\n- Delete a record only if the record with the specified keys satisfies the given conditions\n```clojure\n(st/delete storage {:namespace \"test\"\n                    :table \"testtbl\"\n                    :pk partition-keys\n                    :ck clustering-keys\n                    :condition [[:gt :val1 [100 :int]]\n                                [:lt :val3 [1.0 :double]]]})\n```\n\n### Transaction\n\n```clojure\n(require '[scarab.core :as c])\n(require '[scarab.transaction :as t])\n\n(let [config {:nodes \"192.168.1.32,192.168.1.23,192.168.1.11\"\n              :username \"cassandra\"\n              :password \"cassandra\"}\n      namespace \"testks\"\n      table    \"tx\"\n      tx       (t/start-transaction config)\n      partition-key {:id [1 :int]}\n      values   {:val [111 :int]}]\n\n      (t/put tx {:namespace \"testks\"\n                 :table \"tx\"\n                 :pk partition-keys\n                 :values values})\n      (t/commit tx)\n\n      ; You have to start a new transaction after commit\n      (let [tx (t/start-transaction config)\n            cur-val (first (:val (t/select tx {:namespace \"testks\"\n                                               :table \"tx\"\n                                               :pk partition-keys})))\n            new-val {:val [(+ cur-val 222) :int]}]\n        ; update\n        (t/put tx {:namespace \"testks\"\n                   :table \"tx\"\n                   :pk partition-keys\n                   :values new-val})\n        (t/commit tx))\n```\n\n- First, you need to set up a transaction service with properties\n  ```clojure\n  (t/start-transaction config)\n  ```\n\n- After operating records, you should `commit` the transaction to persist updates\n  ```clojure\n  (t/commit tx)\n  ```\n\n- It is the same as `storage` how to operating records\n  ```clojure\n  (t/select tx {:namespace \"test\"\n                :table \"testtbl\"\n                :pk partition-keys\n                :ck clustering-keys} ;; :ck is optional\n  (t/put tx {:namespace \"test\"\n             :table \"testtbl\"\n             :pk partition-keys\n             :ck clustering-keys ;; :ck is optional\n             :values values})\n  (t/delete tx {:namespace \"test\"\n                :table \"testtbl\"\n                :pk partition-keys\n                :ck clustering-keys}) ;; :ck is optional\n  ```\n\n### 2PC Transaction\n\n```clojure\n(require '[scarab.transaction :as t])\n\n(let [tx (t/start-two-phase-transaction config)\n      pk {:id [1 :int]}]\n  (t/put tx {:namespace \"testks\"\n             :table \"tx\"\n             :pk pk\n             :values {:val [100 :int]}})\n  (t/prepare tx)\n  (t/validate tx)\n  (t/commit tx))\n```\n\n- You can get the transaction id with `(t/id tx)`.\n- You can join or resume an existing 2PC transaction:\n  - `(t/join-two-phase-transaction config tx-id)`\n  - `(t/resume-two-phase-transaction config tx-id)`\n- You can cancel a transaction with `(t/rollback tx)` or `(t/abort tx)`.\n\n## License\n\nCopyright © 2019-20 Yuji Ito\n\nDistributed under [Apache Public License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyito88%2Fscarab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyito88%2Fscarab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyito88%2Fscarab/lists"}