Skip to content

Commit af80634

Browse files
committed
Merge pull request #16 from superprat/patch-1
Added Flush Parameter
2 parents 19eafc1 + 82a1f0b commit af80634

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ dynamodump
33

44
Simple backup and restore script for Amazon DynamoDB using boto to work similarly to mysqldump.
55

6-
Suitable for DynamoDB usages of smaller data volume which do not warrant the usage of AWS Data Pipeline for backup/restores.
6+
Suitable for DynamoDB usages of smaller data volume which do not warrant the usage of AWS Data Pipeline for backup/restores/empty.
77

88
dynamodump supports local DynamoDB instances as well (tested with [dynalite](https://github.com/mhart/dynalite)).
99

@@ -21,7 +21,7 @@ Simple DynamoDB backup/restore.
2121
2222
optional arguments:
2323
-h, --help show this help message and exit
24-
-m MODE, --mode MODE 'backup' or 'restore'
24+
-m MODE, --mode MODE 'backup' or 'restore' or 'empty'
2525
-r REGION, --region REGION
2626
AWS region to use, e.g. 'us-west-1'. Use 'local' for
2727
local DynamoDB testing.

dynamodump.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,51 @@ def update_provisioned_throughput(conn, table_name, read_capacity, write_capacit
162162
if wait:
163163
wait_for_active_table(conn, table_name, "updated")
164164

165+
def do_empty(conn, table_name):
166+
logging.info("Starting Empty for " + table_name + "..")
167+
168+
# get table schema
169+
logging.info("Fetching table schema for " + table_name)
170+
table_data = conn.describe_table(table_name)
171+
172+
table_desc = table_data["Table"]
173+
table_attribute_definitions = table_desc["AttributeDefinitions"]
174+
table_key_schema = table_desc["KeySchema"]
175+
original_read_capacity = table_desc["ProvisionedThroughput"]["ReadCapacityUnits"]
176+
original_write_capacity = table_desc["ProvisionedThroughput"]["WriteCapacityUnits"]
177+
table_local_secondary_indexes = table_desc.get("LocalSecondaryIndexes")
178+
table_global_secondary_indexes = table_desc.get("GlobalSecondaryIndexes")
179+
180+
table_provisioned_throughput = {"ReadCapacityUnits": int(original_read_capacity), "WriteCapacityUnits": int(original_write_capacity)}
181+
182+
logging.info("Deleting Table " + table_name)
183+
184+
delete_table(conn, sleep_interval, table_name)
185+
186+
logging.info("Creating Table " + table_name)
187+
188+
while True:
189+
try:
190+
conn.create_table(table_attribute_definitions, table_name, table_key_schema, table_provisioned_throughput, table_local_secondary_indexes, table_global_secondary_indexes)
191+
break
192+
except boto.exception.JSONResponseError, e:
193+
if e.body["__type"] == "com.amazonaws.dynamodb.v20120810#LimitExceededException":
194+
logging.info("Limit exceeded, retrying creation of " + destination_table + "..")
195+
time.sleep(sleep_interval)
196+
elif e.body["__type"] == "com.amazon.coral.availability#ThrottlingException":
197+
logging.info("Control plane limit exceeded, retrying creation of " + destination_table + "..")
198+
time.sleep(sleep_interval)
199+
else:
200+
logging.exception(e)
201+
sys.exit(1)
202+
203+
# wait for table creation completion
204+
wait_for_active_table(conn, table_name, "created")
205+
206+
207+
208+
logging.info("Recreation of " + table_name + " completed. Time taken: " + str(datetime.datetime.now().replace(microsecond=0) - start_time))
209+
165210
def do_backup(conn, table_name, read_capacity):
166211
logging.info("Starting backup for " + table_name + "..")
167212

@@ -330,8 +375,8 @@ def do_restore(conn, sleep_interval, source_table, destination_table, write_capa
330375
logging.info("Restore for " + source_table + " to " + destination_table + " table completed. Time taken: " + str(datetime.datetime.now().replace(microsecond=0) - start_time))
331376

332377
# parse args
333-
parser = argparse.ArgumentParser(description="Simple DynamoDB backup/restore.")
334-
parser.add_argument("-m", "--mode", help="'backup' or 'restore'")
378+
parser = argparse.ArgumentParser(description="Simple DynamoDB backup/restore/empty.")
379+
parser.add_argument("-m", "--mode", help="'backup' or 'restore' or 'empty'")
335380
parser.add_argument("-r", "--region", help="AWS region to use, e.g. 'us-west-1'. Use '" + LOCAL_REGION + "' for local DynamoDB testing.")
336381
parser.add_argument("-s", "--srcTable", help="Source DynamoDB table name to backup or restore from, use 'tablename*' for wildcard prefix selection or '*' for all tables.")
337382
parser.add_argument("-d", "--destTable", help="Destination DynamoDB table name to backup or restore to, use 'tablename*' for wildcard prefix selection (defaults to use '-' separator) [optional, defaults to source]")
@@ -430,4 +475,22 @@ def do_restore(conn, sleep_interval, source_table, destination_table, write_capa
430475
else:
431476
delete_table(conn, sleep_interval, dest_table)
432477
do_restore(conn, sleep_interval, args.srcTable, dest_table, args.writeCapacity)
478+
elif args.mode == "empty":
479+
if args.srcTable.find("*") != -1:
480+
matching_backup_tables = get_table_name_matches(conn, args.srcTable, prefix_separator)
481+
logging.info("Found " + str(len(matching_backup_tables)) + " table(s) in DynamoDB host to empty: " + ", ".join(matching_backup_tables))
482+
483+
threads = []
484+
for table_name in matching_backup_tables:
485+
t = threading.Thread(target=do_empty, args=(conn, table_name))
486+
threads.append(t)
487+
t.start()
488+
time.sleep(THREAD_START_DELAY)
489+
490+
for thread in threads:
491+
thread.join()
492+
493+
logging.info("Empty of table(s) " + args.srcTable + " completed!")
494+
else:
495+
do_empty(conn, args.srcTable)
433496

0 commit comments

Comments
 (0)