altcodepro-polydb-python 2.3.2__py3-none-any.whl → 2.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/METADATA +1 -1
- {altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/RECORD +6 -6
- polydb/adapters/AzureTableStorageAdapter.py +87 -23
- {altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/WHEEL +0 -0
- {altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/licenses/LICENSE +0 -0
- {altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/top_level.txt +0 -0
{altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: altcodepro-polydb-python
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.4
|
|
4
4
|
Summary: Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety
|
|
5
5
|
Author: AltCodePro
|
|
6
6
|
Project-URL: Homepage, https://github.com/altcodepro/polydb-python
|
{altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/RECORD
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
altcodepro_polydb_python-2.3.
|
|
1
|
+
altcodepro_polydb_python-2.3.4.dist-info/licenses/LICENSE,sha256=9X8GLocsBwy-5aR5JGOt2SAMDDPs9Qv-YnqmHBHOXrw,1067
|
|
2
2
|
polydb/PolyDB.py,sha256=p9eDdvBGosE4fNSSAbtq3tHObdKZ-C2V2Q_ia39Ackk,23397
|
|
3
3
|
polydb/__init__.py,sha256=UhUzfSvmMgKbV2tSME1ooIyfshIBi7_WyU4xl1tWWiA,1454
|
|
4
4
|
polydb/advanced_query.py,sha256=cxMB-EB-qT3bWXJlhmjnMCUtrzogORWyoEfS50Dy7go,4280
|
|
@@ -24,7 +24,7 @@ polydb/validation.py,sha256=a1o1d02k3c6PWQwkBbw_0nEmIgrdB5RR8OcpNQMn4cA,4810
|
|
|
24
24
|
polydb/adapters/AzureBlobStorageAdapter.py,sha256=iNscCIeoWy4YJo3IbI7gNam-NUT7t45ayrlPnYfqlCQ,6409
|
|
25
25
|
polydb/adapters/AzureFileStorageAdapter.py,sha256=OuZY5P-FTQ36954obJN65oSMqmW3d-7QBmXxVGX0lds,6086
|
|
26
26
|
polydb/adapters/AzureQueueAdapter.py,sha256=5tslwI0DgvMeb20w57aVSSSKiuCJEKrYjN0kGuFcdUI,5276
|
|
27
|
-
polydb/adapters/AzureTableStorageAdapter.py,sha256=
|
|
27
|
+
polydb/adapters/AzureTableStorageAdapter.py,sha256=wc_EcrwZmc5M6321Y6tio1BYiPEtJIck9gu6BqQDMsY,21890
|
|
28
28
|
polydb/adapters/BlockchainBlobAdapter.py,sha256=BXSDT6rDGGE04qM2-dVNAeWk-VcF82JGHAdUJeYHCbI,3320
|
|
29
29
|
polydb/adapters/BlockchainKVAdapter.py,sha256=5Egic8QyulgYcy9O12iWKq2EDyVEvnXp_ereYgIvbHk,4546
|
|
30
30
|
polydb/adapters/BlockchainQueueAdapter.py,sha256=K01klT8Eu8c-y1G9Sg2r0PIjay_at9x27kCTTEHPkNY,4179
|
|
@@ -52,7 +52,7 @@ polydb/base/ObjectStorageAdapter.py,sha256=mNdJnhoB3VqSCQvmcoel5PohrVQw7Nrajdd5s
|
|
|
52
52
|
polydb/base/QueueAdapter.py,sha256=jFgyG-SUK4nhRNxm2NbzUbwnA9b_5iAC-ikLSUpXRwk,799
|
|
53
53
|
polydb/base/SharedFilesAdapter.py,sha256=hvmdNNhNxpN46Ob9RLAi8l46GB6JolYyZWnAMuaJ86g,708
|
|
54
54
|
polydb/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
-
altcodepro_polydb_python-2.3.
|
|
56
|
-
altcodepro_polydb_python-2.3.
|
|
57
|
-
altcodepro_polydb_python-2.3.
|
|
58
|
-
altcodepro_polydb_python-2.3.
|
|
55
|
+
altcodepro_polydb_python-2.3.4.dist-info/METADATA,sha256=lbV1IUtpKJ34lC4ViBv1RNcpLk8IPujTe6eU6HO-tnY,11910
|
|
56
|
+
altcodepro_polydb_python-2.3.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
57
|
+
altcodepro_polydb_python-2.3.4.dist-info/top_level.txt,sha256=WgLFWJoYjUhwvyPxJFl6jYLrVFuBJDX3OABf4ocwk_E,7
|
|
58
|
+
altcodepro_polydb_python-2.3.4.dist-info/RECORD,,
|
|
@@ -283,11 +283,22 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
283
283
|
|
|
284
284
|
return out
|
|
285
285
|
|
|
286
|
+
def _sanitize_blob_part(self, value: str) -> str:
|
|
287
|
+
# Blob-safe: lowercase, alphanumeric + dash only
|
|
288
|
+
s = str(value).lower()
|
|
289
|
+
s = re.sub(r"[^a-z0-9\-]", "-", s)
|
|
290
|
+
s = re.sub(r"-+", "-", s)
|
|
291
|
+
return s.strip("-")[:100]
|
|
292
|
+
|
|
286
293
|
def _entity_size_bytes(self, entity: JsonDict) -> int:
|
|
287
294
|
return len(json.dumps(entity, default=json_safe).encode("utf-8"))
|
|
288
295
|
|
|
289
296
|
def _blob_key(self, pk: str, rk: str, checksum: str) -> str:
|
|
290
|
-
|
|
297
|
+
safe_pk = self._sanitize_blob_part(pk)
|
|
298
|
+
safe_rk = self._sanitize_blob_part(rk)
|
|
299
|
+
|
|
300
|
+
# Flat structure (avoid deep paths)
|
|
301
|
+
return f"{safe_pk}-{safe_rk}-{checksum}.json"
|
|
291
302
|
|
|
292
303
|
def _blob_upload(self, blob_key: str, data_bytes: bytes):
|
|
293
304
|
if not self._blob_service:
|
|
@@ -325,27 +336,52 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
325
336
|
|
|
326
337
|
entity = self._pack_entity(model, safe_pk, safe_rk, data)
|
|
327
338
|
|
|
328
|
-
#
|
|
339
|
+
# ---------------------------------------------------
|
|
340
|
+
# SIZE ESTIMATION (Azure uses UTF-16 for strings)
|
|
341
|
+
# ---------------------------------------------------
|
|
342
|
+
MAX_PROPERTY_CHARS = 30 * 1024 # ~30K chars safe under 32K limit
|
|
343
|
+
MAX_ENTITY_SIZE = 40 * 1024 # conservative total entity threshold
|
|
344
|
+
|
|
345
|
+
def _is_large_string(val: Any) -> bool:
|
|
346
|
+
return isinstance(val, str) and len(val) > MAX_PROPERTY_CHARS
|
|
347
|
+
|
|
348
|
+
# Check if any property exceeds safe char limit
|
|
329
349
|
has_large_property = False
|
|
350
|
+
large_key = None
|
|
351
|
+
|
|
330
352
|
for key, value in entity.items():
|
|
331
|
-
if (
|
|
332
|
-
isinstance(value, str) and len(value.encode("utf-8")) > 60 * 1024
|
|
333
|
-
): # 60KB safe margin
|
|
353
|
+
if _is_large_string(value):
|
|
334
354
|
has_large_property = True
|
|
335
|
-
|
|
336
|
-
|
|
355
|
+
large_key = key
|
|
356
|
+
logger.warning(
|
|
357
|
+
f"LARGE PROPERTY DETECTED: {key} length={len(value)} chars → forcing blob overflow"
|
|
337
358
|
)
|
|
338
359
|
break
|
|
339
360
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
361
|
+
# Check full entity size
|
|
362
|
+
entity_json = json.dumps(entity, default=json_safe)
|
|
363
|
+
entity_size = len(entity_json.encode("utf-8"))
|
|
364
|
+
|
|
365
|
+
force_blob = has_large_property or entity_size > MAX_ENTITY_SIZE
|
|
366
|
+
|
|
367
|
+
if force_blob:
|
|
368
|
+
logger.info(
|
|
369
|
+
f"Entity exceeds safe limits → using blob storage "
|
|
370
|
+
f"(size={entity_size//1024} KB, large_key={large_key})"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# ---------------------------------------------------
|
|
374
|
+
# STORE FULL ENTITY IN BLOB
|
|
375
|
+
# ---------------------------------------------------
|
|
376
|
+
full_payload_bytes = entity_json.encode("utf-8")
|
|
343
377
|
checksum = hashlib.md5(full_payload_bytes).hexdigest()
|
|
344
378
|
blob_key = self._blob_key(safe_pk, safe_rk, checksum)
|
|
345
379
|
|
|
346
380
|
self._blob_upload(blob_key, full_payload_bytes)
|
|
347
381
|
|
|
348
|
-
#
|
|
382
|
+
# ---------------------------------------------------
|
|
383
|
+
# STORE REFERENCE IN TABLE
|
|
384
|
+
# ---------------------------------------------------
|
|
349
385
|
reference_entity = {
|
|
350
386
|
"PartitionKey": safe_pk,
|
|
351
387
|
"RowKey": safe_rk,
|
|
@@ -357,33 +393,49 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
357
393
|
"__keymap__": entity.get("__keymap__", "{}"),
|
|
358
394
|
}
|
|
359
395
|
|
|
360
|
-
# Keep
|
|
396
|
+
# Keep only small searchable fields
|
|
361
397
|
for k, v in entity.items():
|
|
362
398
|
if k in ("PartitionKey", "RowKey", "__keymap__", _MODEL_FIELD):
|
|
363
399
|
continue
|
|
364
400
|
if k.startswith("_"):
|
|
365
401
|
continue
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
402
|
+
|
|
403
|
+
if isinstance(v, (str, bool, int, float)):
|
|
404
|
+
if isinstance(v, str) and len(v) > 2000:
|
|
405
|
+
continue
|
|
406
|
+
reference_entity[k] = v
|
|
407
|
+
|
|
408
|
+
elif isinstance(v, datetime):
|
|
371
409
|
reference_entity[k] = v
|
|
372
410
|
|
|
373
411
|
self._table_client.upsert_entity(reference_entity)
|
|
374
|
-
|
|
412
|
+
|
|
413
|
+
logger.info(f"Stored in blob: {blob_key} ({len(full_payload_bytes)//1024} KB)")
|
|
414
|
+
|
|
375
415
|
return {
|
|
376
416
|
"PartitionKey": safe_pk,
|
|
377
417
|
"RowKey": safe_rk,
|
|
378
418
|
"_overflow": True,
|
|
419
|
+
"_blob_key": blob_key,
|
|
379
420
|
"id": safe_rk,
|
|
380
421
|
}
|
|
381
422
|
|
|
382
|
-
#
|
|
423
|
+
# ---------------------------------------------------
|
|
424
|
+
# NORMAL TABLE STORAGE
|
|
425
|
+
# ---------------------------------------------------
|
|
383
426
|
self._table_client.upsert_entity(entity)
|
|
384
|
-
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
"PartitionKey": safe_pk,
|
|
430
|
+
"RowKey": safe_rk,
|
|
431
|
+
"id": safe_rk,
|
|
432
|
+
}
|
|
385
433
|
|
|
386
434
|
except Exception as e:
|
|
435
|
+
# Do NOT retry deterministic size errors
|
|
436
|
+
if "PropertyValueTooLarge" in str(e):
|
|
437
|
+
raise NoSQLError("Azure Table limit exceeded → must use blob overflow") from e
|
|
438
|
+
|
|
387
439
|
raise NoSQLError(f"Azure Table put failed: {str(e)}")
|
|
388
440
|
|
|
389
441
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
@@ -417,6 +469,9 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
417
469
|
|
|
418
470
|
restored = json.loads(blob_data.decode("utf-8"))
|
|
419
471
|
out = self._unpack_entity(restored)
|
|
472
|
+
out["_overflow"] = True
|
|
473
|
+
out["_blob_key"] = blob_key
|
|
474
|
+
out["_checksum"] = checksum
|
|
420
475
|
if "id" not in out:
|
|
421
476
|
out["id"] = safe_rk
|
|
422
477
|
return out
|
|
@@ -489,10 +544,19 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
489
544
|
|
|
490
545
|
if ent_dict.get("_overflow"):
|
|
491
546
|
blob_key = ent_dict.get("_blob_key")
|
|
547
|
+
checksum = ent_dict.get("_checksum")
|
|
492
548
|
if blob_key:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
549
|
+
try:
|
|
550
|
+
blob_data = self._blob_download(blob_key)
|
|
551
|
+
actual_checksum = hashlib.md5(blob_data).hexdigest()
|
|
552
|
+
if checksum and actual_checksum != checksum:
|
|
553
|
+
raise NoSQLError("Checksum mismatch")
|
|
554
|
+
|
|
555
|
+
restored = json.loads(blob_data.decode("utf-8"))
|
|
556
|
+
out = self._unpack_entity(restored)
|
|
557
|
+
except Exception as e:
|
|
558
|
+
logger.error(f"Blob read failed, falling back to table: {e}")
|
|
559
|
+
out = self._unpack_entity(ent_dict)
|
|
496
560
|
else:
|
|
497
561
|
out = self._unpack_entity(ent_dict)
|
|
498
562
|
else:
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.2.dist-info → altcodepro_polydb_python-2.3.4.dist-info}/top_level.txt
RENAMED
|
File without changes
|