1- import uuid
1+ import logging
2+ import os
23import time
4+ import uuid
5+
36from eventsourcingdb .client import Client
47from eventsourcingdb .container import Container
58from .testing_database import TestingDatabase
6- import os
79
810
911class Database :
@@ -21,82 +23,95 @@ def __init__(
2123 self .with_authorization : TestingDatabase = with_authorization
2224 self .with_invalid_url : TestingDatabase = with_invalid_url
2325
24- @classmethod
25- async def create (cls , max_retries = 3 , retry_delay = 2.0 ) -> 'Database' :
26- """Create a new Database instance with retry mechanism for container startup"""
27- api_token = str (uuid .uuid4 ())
28-
29- dockerfile_path = os .path .join (
30- os .path .dirname (__file__ ),
31- 'docker/eventsourcingdb/Dockerfile' )
32- with open (dockerfile_path , 'r' ) as dockerfile :
33- content = dockerfile .read ().strip ()
34- image_tag = content .split (':' )[- 1 ]
35-
36- container = Container (
26+ @staticmethod
27+ def _create_container (api_token , image_tag ):
28+ return Container (
3729 image_name = "thenativeweb/eventsourcingdb" ,
3830 image_tag = image_tag ,
3931 api_token = api_token ,
4032 internal_port = 3000
4133 )
4234
43- # Try with retries
35+ @staticmethod
36+ async def _initialize_clients (container , api_token ):
37+ with_authorization_client = container .get_client ()
38+ await with_authorization_client .initialize ()
39+
40+ with_authorization = TestingDatabase (
41+ with_authorization_client ,
42+ container
43+ )
44+
45+ with_invalid_url_client = Client (
46+ base_url = 'http://localhost.invalid' ,
47+ api_token = api_token
48+ )
49+ await with_invalid_url_client .initialize ()
50+
51+ with_invalid_url = TestingDatabase (
52+ with_invalid_url_client
53+ )
54+ return with_authorization , with_invalid_url
55+
56+ @staticmethod
57+ def _stop_container_safely (container ):
58+ """Safely stop a container, ignoring errors"""
59+ try :
60+ container .stop ()
61+ except OSError :
62+ pass
63+
64+ @classmethod
65+ async def create (cls , max_retries = 3 , retry_delay = 2.0 ) -> 'Database' :
66+ api_token = str (uuid .uuid4 ())
67+ image_tag = cls ._get_image_tag_from_dockerfile ()
68+ container = cls ._create_container (api_token , image_tag )
69+ error = None
70+
4471 for attempt in range (max_retries ):
4572 try :
46- # Start the container with timeout handling
4773 container .start ()
48-
49- # Create client with authorization
50- with_authorization_client = container .get_client ()
51- await with_authorization_client .initialize ()
52- with_authorization = TestingDatabase (
53- with_authorization_client ,
54- container # Pass container to TestingDatabase for cleanup
74+ except OSError as caught_error :
75+ error = caught_error
76+ retry = True
77+ except Exception as unexpected_error :
78+ cls ._stop_container_safely (container )
79+ raise unexpected_error
80+ else :
81+ retry = False
82+
83+ if retry :
84+ if attempt == max_retries - 1 :
85+ cls ._stop_container_safely (container )
86+ msg = f"Failed to initialize database container after { max_retries } attempts"
87+ raise RuntimeError (f"{ msg } : { error } " ) from error
88+ logging .warning (
89+ "Container startup attempt %d failed: %s. Retrying in %s seconds..." ,
90+ attempt + 1 , error , retry_delay
5591 )
92+ time .sleep (retry_delay )
93+ cls ._stop_container_safely (container )
94+ container = cls ._create_container (api_token , "latest" )
95+ continue
5696
57- # Create client with invalid URL but valid token
58- with_invalid_url_client = Client (
59- base_url = 'http://localhost.invalid' ,
60- api_token = api_token
61- )
62- await with_invalid_url_client .initialize ()
63- with_invalid_url = TestingDatabase (
64- with_invalid_url_client
65- )
97+ try :
98+ auth_db , invalid_url_db = await cls ._initialize_clients (container , api_token )
99+ except Exception as client_error :
100+ cls ._stop_container_safely (container )
101+ raise client_error
66102
67- return cls (Database .__create_key , with_authorization , with_invalid_url )
103+ return cls (Database .__create_key , auth_db , invalid_url_db )
68104
69- except Exception as e :
70- # On the last attempt, raise the error
71- if attempt == max_retries - 1 :
72- # Cleanup the container if it was created
73- try :
74- container .stop ()
75- except BaseException :
76- pass
77- raise RuntimeError (
78- f"Failed to initialize database container after { max_retries } attempts: { e } " )
79-
80- # Otherwise wait and retry
81- print (
82- f"Container startup attempt {
83- attempt +
84- 1 } failed: { e } . Retrying in { retry_delay } seconds..." )
85- time .sleep (retry_delay )
105+ raise RuntimeError ("Failed to create database: Unexpected error during retry loop" )
86106
87- # Try to clean up the failed container before retrying
88- try :
89- container .stop ()
90- except BaseException :
91- pass
92-
93- # Create a new container for the next attempt
94- container = Container (
95- image_name = "thenativeweb/eventsourcingdb" ,
96- image_tag = "latest" ,
97- api_token = api_token ,
98- internal_port = 3000
99- )
107+ @staticmethod
108+ def _get_image_tag_from_dockerfile ():
109+ dockerfile_path = os .path .join (
110+ os .path .dirname (__file__ ),
111+ 'docker/eventsourcingdb/Dockerfile' )
112+ with open (dockerfile_path , 'r' , encoding = 'utf-8' ) as dockerfile :
113+ content = dockerfile .read ().strip ()
114+ return content .split (':' )[- 1 ]
100115
101116 async def stop (self ):
102117 await self .with_authorization .stop ()
0 commit comments