Use a mutex for registry CLI txs in webapp deployment commands #957
| @ -360,6 +360,9 @@ def dump_known_requests(filename, requests, status="SEEN"): | ||||
| @click.option( | ||||
|     "--private-key-file", help="The private key for decrypting config.", required=True | ||||
| ) | ||||
| @click.option( | ||||
|     "--registry-lock-file", help="File path to use for registry mutex lock", default=None | ||||
| ) | ||||
| @click.option( | ||||
|     "--private-key-passphrase", | ||||
|     help="The passphrase for the private key.", | ||||
| @ -393,6 +396,7 @@ def command(  # noqa: C901 | ||||
|     private_key_passphrase, | ||||
|     all_requests, | ||||
|     auction_requests, | ||||
|     registry_lock_file, | ||||
| ): | ||||
|     if request_id and discover: | ||||
|         print("Cannot specify both --request-id and --discover", file=sys.stderr) | ||||
| @ -444,7 +448,7 @@ def command(  # noqa: C901 | ||||
|         include_tags = [tag.strip() for tag in include_tags.split(",") if tag] | ||||
|         exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag] | ||||
| 
 | ||||
|         laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr) | ||||
|         laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr, mutex_lock_file=registry_lock_file) | ||||
|         webapp_deployer_record = laconic.get_record(lrn, require=True) | ||||
|         payment_address = webapp_deployer_record.attributes.paymentAddress | ||||
|         main_logger.log(f"Payment address: {payment_address}") | ||||
| @ -649,7 +653,7 @@ def command(  # noqa: C901 | ||||
|                         ) | ||||
|                         run_log_file = open(run_log_file_path, "wt") | ||||
|                         run_reg_client = LaconicRegistryClient( | ||||
|                             laconic_config, log_file=run_log_file | ||||
|                             laconic_config, log_file=run_log_file, mutex_lock_file=registry_lock_file | ||||
|                         ) | ||||
| 
 | ||||
|                     build_logger = TimedLogger(run_id, run_log_file) | ||||
|  | ||||
| @ -120,6 +120,9 @@ def dump_known_auction_requests(filename, requests, status="SEEN"): | ||||
|     help="Bid to place on application deployment auctions (in alnt)", | ||||
|     required=True, | ||||
| ) | ||||
| @click.option( | ||||
|     "--registry-lock-file", help="File path to use for registry mutex lock", default=None | ||||
| ) | ||||
| @click.option( | ||||
|     "--dry-run", help="Don't do anything, just report what would be done.", is_flag=True | ||||
| ) | ||||
| @ -129,6 +132,7 @@ def command( | ||||
|     laconic_config, | ||||
|     state_file, | ||||
|     bid_amount, | ||||
|     registry_lock_file, | ||||
|     dry_run, | ||||
| ): | ||||
|     if int(bid_amount) < 0: | ||||
| @ -138,7 +142,7 @@ def command( | ||||
|     logger = TimedLogger(file=sys.stderr) | ||||
| 
 | ||||
|     try: | ||||
|         laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr) | ||||
|         laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr, mutex_lock_file=registry_lock_file) | ||||
|         auctions_requests = laconic.app_deployment_auctions() | ||||
| 
 | ||||
|         previous_requests = {} | ||||
|  | ||||
| @ -102,7 +102,7 @@ def command( | ||||
|         "max_price": max_price, | ||||
|         "num_providers": num_providers, | ||||
|     } | ||||
|     auction_id = laconic.create_auction(provider_auction_params) | ||||
|     auction_id = laconic.create_deployment_auction(provider_auction_params) | ||||
|     print("Deployment auction created:", auction_id) | ||||
| 
 | ||||
|     if not auction_id: | ||||
|  | ||||
							
								
								
									
										31
									
								
								stack_orchestrator/deploy/webapp/registry_mutex.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								stack_orchestrator/deploy/webapp/registry_mutex.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| import fcntl | ||||
| from functools import wraps | ||||
| 
 | ||||
| # Define default file path for the lock | ||||
| DEFAULT_LOCK_FILE_PATH = "/tmp/registry_mutex_lock_file" | ||||
| 
 | ||||
| 
 | ||||
| def registry_mutex(): | ||||
|     def decorator(func): | ||||
|         @wraps(func) | ||||
|         def wrapper(self, *args, **kwargs): | ||||
|             lock_file_path = DEFAULT_LOCK_FILE_PATH | ||||
|             if self.mutex_lock_file is not None: | ||||
|                 lock_file_path = self.mutex_lock_file | ||||
| 
 | ||||
|             with open(lock_file_path, 'w') as lock_file: | ||||
|                 try: | ||||
|                     # Try to acquire the lock | ||||
|                     fcntl.flock(lock_file, fcntl.LOCK_EX) | ||||
| 
 | ||||
|                     # Call the actual function | ||||
|                     result = func(self, *args, **kwargs) | ||||
|                 finally: | ||||
|                     # Always release the lock | ||||
|                     fcntl.flock(lock_file, fcntl.LOCK_UN) | ||||
| 
 | ||||
|             return result | ||||
| 
 | ||||
|         return wrapper | ||||
| 
 | ||||
|     return decorator | ||||
| @ -178,6 +178,9 @@ def dump_known_requests(filename, requests): | ||||
|     "my payment address are examined).", | ||||
|     is_flag=True, | ||||
| ) | ||||
| @click.option( | ||||
|     "--registry-lock-file", help="File path to use for registry mutex lock", default=None | ||||
| ) | ||||
| @click.pass_context | ||||
| def command(  # noqa: C901 | ||||
|     ctx, | ||||
| @ -195,6 +198,7 @@ def command(  # noqa: C901 | ||||
|     min_required_payment, | ||||
|     lrn, | ||||
|     all_requests, | ||||
|     registry_lock_file, | ||||
| ): | ||||
|     if request_id and discover: | ||||
|         print("Cannot specify both --request-id and --discover", file=sys.stderr) | ||||
| @ -212,7 +216,7 @@ def command(  # noqa: C901 | ||||
|     include_tags = [tag.strip() for tag in include_tags.split(",") if tag] | ||||
|     exclude_tags = [tag.strip() for tag in exclude_tags.split(",") if tag] | ||||
| 
 | ||||
|     laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr) | ||||
|     laconic = LaconicRegistryClient(laconic_config, log_file=sys.stderr, mutex_lock_file=registry_lock_file) | ||||
|     deployer_record = laconic.get_record(lrn, require=True) | ||||
|     payment_address = deployer_record.attributes.paymentAddress | ||||
|     main_logger.log(f"Payment address: {payment_address}") | ||||
|  | ||||
| @ -26,6 +26,8 @@ import yaml | ||||
| 
 | ||||
| from enum import Enum | ||||
| 
 | ||||
| from stack_orchestrator.deploy.webapp.registry_mutex import registry_mutex | ||||
| 
 | ||||
| 
 | ||||
| class AuctionStatus(str, Enum): | ||||
|     COMMIT = "commit" | ||||
| @ -391,6 +393,7 @@ class LaconicRegistryClient: | ||||
|         criteria["type"] = "ApplicationDeploymentAuction" | ||||
|         return self.list_records(criteria, all) | ||||
| 
 | ||||
|     @registry_mutex() | ||||
|     def publish(self, record, names=None): | ||||
|         if names is None: | ||||
|             names = [] | ||||
| @ -421,6 +424,7 @@ class LaconicRegistryClient: | ||||
|         finally: | ||||
|             logged_cmd(self.log_file, "rm", "-rf", tmpdir) | ||||
| 
 | ||||
|     @registry_mutex() | ||||
|     def set_name(self, name, record_id): | ||||
|         logged_cmd( | ||||
|             self.log_file, | ||||
| @ -434,6 +438,7 @@ class LaconicRegistryClient: | ||||
|             record_id, | ||||
|         ) | ||||
| 
 | ||||
|     @registry_mutex() | ||||
|     def delete_name(self, name): | ||||
|         logged_cmd( | ||||
|             self.log_file, | ||||
| @ -446,6 +451,7 @@ class LaconicRegistryClient: | ||||
|             name, | ||||
|         ) | ||||
| 
 | ||||
|     @registry_mutex() | ||||
|     def send_tokens(self, address, amount, type="alnt"): | ||||
|         args = [ | ||||
|             "laconic", | ||||
| @ -464,58 +470,36 @@ class LaconicRegistryClient: | ||||
| 
 | ||||
|         return AttrDict(json.loads(logged_cmd(self.log_file, *args))) | ||||
| 
 | ||||
|     def create_auction(self, auction): | ||||
|         if auction["kind"] == AUCTION_KIND_PROVIDER: | ||||
|             args = [ | ||||
|                 "laconic", | ||||
|                 "-c", | ||||
|                 self.config_file, | ||||
|                 "registry", | ||||
|                 "auction", | ||||
|                 "create", | ||||
|                 "--kind", | ||||
|                 auction["kind"], | ||||
|                 "--commits-duration", | ||||
|                 str(auction["commits_duration"]), | ||||
|                 "--reveals-duration", | ||||
|                 str(auction["reveals_duration"]), | ||||
|                 "--denom", | ||||
|                 auction["denom"], | ||||
|                 "--commit-fee", | ||||
|                 str(auction["commit_fee"]), | ||||
|                 "--reveal-fee", | ||||
|                 str(auction["reveal_fee"]), | ||||
|                 "--max-price", | ||||
|                 str(auction["max_price"]), | ||||
|                 "--num-providers", | ||||
|                 str(auction["num_providers"]) | ||||
|             ] | ||||
|         else: | ||||
|             args = [ | ||||
|                 "laconic", | ||||
|                 "-c", | ||||
|                 self.config_file, | ||||
|                 "registry", | ||||
|                 "auction", | ||||
|                 "create", | ||||
|                 "--kind", | ||||
|                 auction["kind"], | ||||
|                 "--commits-duration", | ||||
|                 str(auction["commits_duration"]), | ||||
|                 "--reveals-duration", | ||||
|                 str(auction["reveals_duration"]), | ||||
|                 "--denom", | ||||
|                 auction["denom"], | ||||
|                 "--commit-fee", | ||||
|                 str(auction["commit_fee"]), | ||||
|                 "--reveal-fee", | ||||
|                 str(auction["reveal_fee"]), | ||||
|                 "--minimum-bid", | ||||
|                 str(auction["minimum_bid"]) | ||||
|             ] | ||||
|     @registry_mutex() | ||||
|     def create_deployment_auction(self, auction): | ||||
|         args = [ | ||||
|             "laconic", | ||||
|             "-c", | ||||
|             self.config_file, | ||||
|             "registry", | ||||
|             "auction", | ||||
|             "create", | ||||
|             "--kind", | ||||
|             auction["kind"], | ||||
|             "--commits-duration", | ||||
|             str(auction["commits_duration"]), | ||||
|             "--reveals-duration", | ||||
|             str(auction["reveals_duration"]), | ||||
|             "--denom", | ||||
|             auction["denom"], | ||||
|             "--commit-fee", | ||||
|             str(auction["commit_fee"]), | ||||
|             "--reveal-fee", | ||||
|             str(auction["reveal_fee"]), | ||||
|             "--max-price", | ||||
|             str(auction["max_price"]), | ||||
|             "--num-providers", | ||||
|             str(auction["num_providers"]) | ||||
|         ] | ||||
| 
 | ||||
|         return json.loads(logged_cmd(self.log_file, *args))["auctionId"] | ||||
| 
 | ||||
|     @registry_mutex() | ||||
|     def commit_bid(self, auction_id, amount, type="alnt"): | ||||
|         args = [ | ||||
|             "laconic", | ||||
| @ -532,6 +516,7 @@ class LaconicRegistryClient: | ||||
| 
 | ||||
|         return json.loads(logged_cmd(self.log_file, *args))["reveal_file"] | ||||
| 
 | ||||
|     @registry_mutex() | ||||
|     def reveal_bid(self, auction_id, reveal_file_path): | ||||
|         logged_cmd( | ||||
|             self.log_file, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user