{"id":21630470,"url":"https://github.com/hawkular/cassalog","last_synced_at":"2025-06-15T15:04:31.474Z","repository":{"id":78832561,"uuid":"50209157","full_name":"hawkular/cassalog","owner":"hawkular","description":"A Cassandra schema change management tool for applications running on the JVM","archived":false,"fork":false,"pushed_at":"2018-04-19T20:48:33.000Z","size":100,"stargazers_count":14,"open_issues_count":4,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-25T10:12:05.606Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Groovy","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/hawkular.png","metadata":{"files":{"readme":"README.adoc","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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-01-22T21:41:41.000Z","updated_at":"2021-05-27T18:41:44.000Z","dependencies_parsed_at":"2023-03-12T05:58:17.194Z","dependency_job_id":null,"html_url":"https://github.com/hawkular/cassalog","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hawkular%2Fcassalog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hawkular%2Fcassalog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hawkular%2Fcassalog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hawkular%2Fcassalog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hawkular","download_url":"https://codeload.github.com/hawkular/cassalog/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248412948,"owners_count":21099225,"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":[],"created_at":"2024-11-25T02:11:06.089Z","updated_at":"2025-04-11T14:05:22.992Z","avatar_url":"https://github.com/hawkular.png","language":"Groovy","funding_links":[],"categories":["Packages"],"sub_categories":["Tools"],"readme":"= Cassalog\n\nCassalog is a schema change management library and tool for\nhttp://cassandra.apache.org[Apache Cassandra] that can be used with\napplications running on the JVM.\n\n== Why?\nJust as application code evolves and changes so do database schemas. If you are\nbuilding an application and intend to support upgrading from one version to\nanother, then managing schema changes is essential. If you are lucky, you might\nbe able to get by with running some simple upgrade scripts to bring the schema\nup to date with the new version. This likely will not work however if you\nsupport multiple upgrade paths. For example, suppose we have versions 1 and 2,\nand are introducing version 3 of an application. We want to allow upgrading to\nversion 3 from either 1 or 2 in addition to upgrading from 1 to 2.\n\nYou could add schema upgrade logic to application code, but that is often a\nless that ideal solution as it convolutes the code base. Fortunately, there are\ntool for managing schema changes like http://www.liquibase.org/[Liquibase],\nhttp://flywaydb.org/[Flyway], and\nhttp://guides.rubyonrails.org/active_record_basics.html[Active Record] for Ruby\non Rails applications. These tools however, are designed specifically for\nrelational databases. I previously spent time trying to patch Liquibase to\nsupport Cassandra but found that it was not a good fit. Cassalog is designed\nsolely for use with Cassandra, not for any other database systems.\n\nCassalog is written in Groovy. There are several reasons for this. First,\nGroovy offers great interoperability with Java, making it usable and accessible\nto application running on the JVM. Groovy's dynamic and meta programming\nfeatures make it easy to write domain specific languages. Groovy has multi-line\nstrings and string interpolation out of the box, both of which can be really\nuseful for writing schema change scripts. Lastly, with Cassalog schema changes\nare not written in XML or JSON. Instead they are written as Groovy scripts\ngiving you the full power and flexibility of Groovy.\n\n== Usage\nThe Cassalog class is the primary class with which you will interact.\n\n[source,groovy]\n----\n// Groovy\ndef script = // load schema change script\ndef session = // obtain DataStax driver Session object\ndef cassalog = new Cassalog(session: session)\ncassalog.execute(script)\n----\n\n[source,java]\n----\n// Java\nURI script = // load schema change script\nSession session = // obtain DataStax driver Session object\nCassalog cassalog = new Cassalog();\ncassalog.setSession(session);\ncassalog.execute(script);\n----\n\nAnd here is what a cassalog script might look like,\n\n[source,groovy]\n----\ncreateKeyspace {\n  version '0.1'\n  name 'my_keyspace'\n  author 'admin'\n  description 'Set up a keyspace for unit tests'\n}\n\nschemaChange {\n  version '0.1.1'\n  author 'admin'\n  description 'Create table for storing time series data'\n  cql \"\"\"\nCREATE TABLE metrics (\n    id uuid,\n    time timeuuid,\n    value double,\n    PRIMARY KEY (id, time)\n)\n\"\"\"\n}\n----\n\nTIP: Schema changes are applied in the order that they are declared in the\nscript(s) regardless of the assigned versions.\n\n== Features\n* Tagging\n* Execute arbitrary Groovy / Java code in schema change scripts\n* Pass variables to scripts\n* Changes can stored across multiple scripts\n* Schema change detection\n\n=== Tagging\nYou can specify tags when running Cassalog, e.g.\n\n[source,groovy]\n----\n// Groovy\ndef script = // load schema change script\ndef session = // obtain DataStax driver Session object\ndef cassalog = new Cassalog(session: session)\ncassalog.execute(script, ['dev', 'test_data'])\n----\n\n[source,java]\n----\n// Java\nURI script = // load schema change script\nSession session = // obtain DataStax driver Session object\nCassalog cassalog = new Cassalog();\ncassalog.setSession(session);\ncassalog.execute(script, Collections.asList(\"dev\", \"test_data\"));\n----\n\nCassalog will apply schema changes that have not already been run and that\n\n* Dot not specify any tags or\n* Specify tags and include the `dev` and `test_data` tags\n\n=== Execute arbitrary code\nCassandra is frequently used for time series data. Suppose we have a metrics\ntable, and we want to generate some sample data for tests.\n\n[source,groovy]\n----\nschemaChange {\n  version '1.0'\n  cql \"\"\"\nCREATE TABLE metrics (\n    id text PRIMARY KEY,\n    time timestamp,\n    value double\n)\n\"\"\"\n}\n\ntestData = []\nrandom = new Random\n10.times { i -\u003e\n  testData \u003c\u003c \"INSERT INTO metrics (id, time, value) VALUES ('$i', ${new Date().time + 100}, ${random.nextDouble()})\"\n}\n\nschemaChange {\n  version '1.0.1'\n  tags 'test_data'\n  cql testData\n}\n----\n\nThis script first calls the `schemaChange` function to create the metrics table.\nThe next few lines generate a list of INSERT statements with some test data.\nFinally, we have another call to `schemaChange`. It specifies the test_data\ntag and passes the `testData` list to the `cql` parameter.\n\n=== Pass variables to scripts\nYou can pass arbitrary variables to scripts, not just strings.\n\n[source,groovy]\n----\n// Groovy\ndef vars = [\n  metricIds: ['M1', 'M2', 'M3'],\n  startDate: new Date()\n  maxValue: 100,\n  minValue: 50\n]\ncassalog.execute(script, vars)\n----\n\n[source,java]\n----\n// Java\nMap\u003cString, ?\u003e vars = ImmutableMap.of(\n    \"metricIds\", asList(\"M1\", \"M2\", \"M3\"),\n    \"startDate\", new Date(),\n    \"maxValue\", 100,\n    \"minValue\", 50\n);\ncassalog.execute(script, vars);\n----\n\n=== Changes can stored across multiple scripts\nYou can use the `include` function to store changes in multiple script to\nkeep your schema changes more modular and better organized.\n\n[source,groovy]\n----\ninclude '/dbchanges/base_tables.groovy'\n\ninclude '/dbchanges/seed_data.groovy'\n----\n\nThe `include` function currently takes a single string argument that should\nspecify the absolute path of a script on the classpath or from the configured `baseScriptsPath`.\n\n`baseScriptsPath` is an absolute path to where the other include scripts are located e.g. `/Users/john/cassalog/scripts`.\n\n=== Schema change detection\nCassalog does not store the CQL code associated with each schema change. It\ncomputes a hash of the CQL and stores that instead. If the hash in the change\nlog differs from the hash of the CQL in the source script, Cassalog will throw\na ChangeSetAlteredException.\n\nYou will need to manually resolve the issue that caused the\nChangeSetAlteredException. Cassandra does not support transactions like a\nrelational database, so there no rollback functionality to fall back on.\n\n== Change Log Table\nAll schema changes are recorded in the change log table, _cassalog_. The table\nwill be created the first time Cassalog is run. Change log data looks like,\n\n[noformat]\n----\nbucket | revision | applied_at               | author | description | hash         | version  | tags\n--------+----------+--------------------------+--------+-----------------------------------------------------+\n     0 |        0 | 2016-01-28 11:09:54-0500 | admin | First table  | 0xe361957eeb |      1.0 | {'legacy'}\n     0 |        1 | 2016-01-28 11:09:54-0500 | admin | Second table | 0xf336e725d4 |      1.1 | {'legacy'}\n     0 |        2 | 2016-01-28 11:09:55-0500 | admin | Third table  | 0xcecef5f840 |      1.2 | {'legacy', 'dev'}\n     0 |        3 | 2016-01-28 11:09:55-0500 | admin | Fourth table | 0x4b5d24b77c |      1.3 | {'legacy'}\n----\n\nHere is a brief overview of the schema.\n\n[noformat]\n----\nCREATE TABLE cassalog (\n    bucket int,\n    revision int,\n    applied_at timestamp,\n    author text,\n    description text,\n    hash blob,\n    version text,\n    tags set\u003ctext\u003e,\n    PRIMARY KEY (bucket, revision)\n)\n----\n\n*author* +\nThe username, or email address, etc. of the person making the change. This is\nan optional field and can be null.\n\n*description* +\nA summary of the changes. This is an optional field and can be null.\n\n*hash* +\nCassalog does not store the CQL statements that it executes. Instead it stores a\nhash that uniquely identifies the CQL statement(s). Cassalog generates this\nhash value.\n\n*version* +\nThe version can be an arbitrary string. It should be a unique identifier for the\nchange; however, Cassalog does not enforce uniqueness. This is a required field.\n\n*tags* +\nAn optional set of user-supplied tags.\n\n*revision* +\nCassalog assigns a revision number to each change that it applies. It uses the\nrevision number to keep track of the order in which changes are applied. If the\norder of schema changes in a source script is changed, then a\nChangeSetAlteredException will be thrown.\n\n*bucket* +\nCassalog stores multiple rows per physical partition. This is a revision offset.\nThe bucket size defaults to 100.\n\n== Building from Source\nCassandra is built with Maven and requires a JVM version 1.7 or later. Test\nexecution requires a running Cassandra cluster (which can be a single node) with\na node listening on 127.0.0.1. Cassandra 2.0 or later should be used.\n\n[source,bash]\n----\ngit clone https://github.com/jsanda/cassalog.git\ncd cassalog\nmvn install\n----\n\nTIP: If you want to build without having a running Cassandra instance, you can\nrun `mvn install -DskipTests`\n\n== Setting up Cassandra for development or testing\nThe recommended way to set up Cassandra is by using\nhttps://github.com/pcmanus/ccm[ccm (Cassandra Cluster Manager)].\n\nAs Cassalog evolves and looks to support different versions of Cassandra and\nCQL, ccm is the likely tool of choice to use for testing against different\nversions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhawkular%2Fcassalog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhawkular%2Fcassalog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhawkular%2Fcassalog/lists"}