Skip to content

Commit d95acb2

Browse files
committed
ngdg
1 parent 2b5399d commit d95acb2

30 files changed

+4923
-1121
lines changed

CHANGELOG.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
3.22.0
2+
======
3+
Unreleased
4+
5+
Features
6+
--------
7+
8+
* Parse new schema metadata in NGDG and generate table edges CQL syntax (PYTHON-996)
9+
* Add GraphSON3 support (PYTHON-788)
10+
* Use GraphSON3 as default for Native graphs (PYTHON-1004)
11+
* Add Tuple and UDT types for native graph (PYTHON-1005)
12+
* Add Duration type for native graph (PYTHON-1000)
13+
* Add gx:ByteBuffer graphson type support for Blob field (PYTHON-1027)
14+
* Enable Paging Through DSE Driver for Gremlin Traversals (PYTHON-1045)
15+
* Provide numerical wrappers to ensure proper graphson schema definition (PYTHON-1051)
16+
* Resolve the row_factory automatically for native graphs (PYTHON-1056)
17+
* Add g:TraversalMetrics/g:Metrics graph deserializers (PYTHON-1057)
18+
* Add g:BulkSet graph deserializers (PYTHON-1060)
19+
* Update Graph Engine names and the way to create a Classic/Native Graph (PYTHON-1090)
20+
* Update Native to Core Graph Engine
21+
* Add graphson3 and native graph support (PYTHON-1039)
22+
* Enable Paging Through DSE Driver for Gremlin Traversals (PYTHON-1045)
23+
* Expose filter predicates for cql collections (PYTHON-1019)
24+
* Add g:TraversalMetrics/Metrics deserializers (PYTHON-1057)
25+
126
3.21.0
227
======
328
Unreleased

build.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ schedules:
5555
env_vars: |
5656
EVENT_LOOP_MANAGER='libev'
5757
58+
ngdg:
59+
schedule: adhoc
60+
branches:
61+
include: [ngdg_master_ft]
62+
env_vars: |
63+
EVENT_LOOP_MANAGER='libev'
64+
EXCLUDE_LONG=1
65+
matrix:
66+
exclude:
67+
- python: [2.7, 3.4, 3.6, 3.7]
68+
- cassandra: ['dse-4.8', 'dse-5.0', dse-6.0', 'dse-6.7']
69+
5870
weekly_master:
5971
schedule: 0 10 * * 6
6072
branches:
@@ -166,6 +178,7 @@ cassandra:
166178
- 'dse-5.1'
167179
- 'dse-6.0'
168180
- 'dse-6.7'
181+
- 'dse-6.8'
169182

170183
env:
171184
CYTHON:
@@ -177,6 +190,7 @@ build:
177190
export JAVA_HOME=$CCM_JAVA_HOME
178191
export PATH=$JAVA_HOME/bin:$PATH
179192
export PYTHONPATH=""
193+
export CCM_MAX_HEAP_SIZE=1024M
180194
181195
# Install latest setuptools
182196
pip install --upgrade pip

cassandra/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def emit(self, record):
2222

2323
logging.getLogger('cassandra').addHandler(NullHandler())
2424

25-
__version_info__ = (3, 19, 0, 'post0')
25+
__version_info__ = (3, 19, 0, '20190910+labs')
2626
__version__ = '.'.join(map(str, __version_info__))
2727

2828

cassandra/cluster.py

Lines changed: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,16 @@
8383
from cassandra.marshal import int64_pack
8484
from cassandra.timestamps import MonotonicTimestampGenerator
8585
from cassandra.compat import Mapping
86-
from cassandra.util import _resolve_contact_points_to_string_map
86+
from cassandra.util import _resolve_contact_points_to_string_map, Version
8787

8888
from cassandra.datastax.insights.reporter import MonitorReporter
8989
from cassandra.datastax.insights.util import version_supports_insights
9090

9191
from cassandra.datastax.graph import (graph_object_row_factory, GraphOptions, GraphSON1Serializer,
92-
GraphProtocol, GraphSON2Serializer, GraphStatement, SimpleGraphStatement)
93-
from cassandra.datastax.graph.query import _request_timeout_key
92+
GraphProtocol, GraphSON2Serializer, GraphStatement, SimpleGraphStatement,
93+
graph_graphson2_row_factory, graph_graphson3_row_factory,
94+
GraphSON3Serializer)
95+
from cassandra.datastax.graph.query import _request_timeout_key, _GraphSONContextRowFactory
9496

9597
if six.PY3:
9698
long = int
@@ -141,6 +143,7 @@ def _is_gevent_monkey_patched():
141143
DEFAULT_MIN_CONNECTIONS_PER_REMOTE_HOST = 1
142144
DEFAULT_MAX_CONNECTIONS_PER_REMOTE_HOST = 2
143145

146+
_GRAPH_PAGING_MIN_DSE_VERSION = Version('6.8.0')
144147

145148
_NOT_SET = object()
146149

@@ -395,20 +398,21 @@ class GraphExecutionProfile(ExecutionProfile):
395398

396399
def __init__(self, load_balancing_policy=_NOT_SET, retry_policy=None,
397400
consistency_level=ConsistencyLevel.LOCAL_ONE, serial_consistency_level=None,
398-
request_timeout=30.0, row_factory=graph_object_row_factory,
399-
graph_options=None):
401+
request_timeout=30.0, row_factory=None,
402+
graph_options=None, continuous_paging_options=_NOT_SET):
400403
"""
401404
Default execution profile for graph execution.
402405
403-
See :class:`.ExecutionProfile`
404-
for base attributes.
406+
See :class:`.ExecutionProfile` for base attributes. Note that if not explicitly set,
407+
the row_factory and graph_options.graph_protocol are resolved during the query execution.
405408
406409
In addition to default parameters shown in the signature, this profile also defaults ``retry_policy`` to
407410
:class:`cassandra.policies.NeverRetryPolicy`.
408411
"""
409412
retry_policy = retry_policy or NeverRetryPolicy()
410413
super(GraphExecutionProfile, self).__init__(load_balancing_policy, retry_policy, consistency_level,
411-
serial_consistency_level, request_timeout, row_factory)
414+
serial_consistency_level, request_timeout, row_factory,
415+
continuous_paging_options=continuous_paging_options)
412416
self.graph_options = graph_options or GraphOptions(graph_source=b'g',
413417
graph_language=b'gremlin-groovy')
414418

@@ -417,7 +421,7 @@ class GraphAnalyticsExecutionProfile(GraphExecutionProfile):
417421

418422
def __init__(self, load_balancing_policy=None, retry_policy=None,
419423
consistency_level=ConsistencyLevel.LOCAL_ONE, serial_consistency_level=None,
420-
request_timeout=3600. * 24. * 7., row_factory=graph_object_row_factory,
424+
request_timeout=3600. * 24. * 7., row_factory=None,
421425
graph_options=None):
422426
"""
423427
Execution profile with timeout and load balancing appropriate for graph analytics queries.
@@ -2434,6 +2438,7 @@ def default_serial_consistency_level(self, cl):
24342438
_profile_manager = None
24352439
_metrics = None
24362440
_request_init_callbacks = None
2441+
_graph_paging_available = False
24372442

24382443
def __init__(self, cluster, hosts, keyspace=None):
24392444
self.cluster = cluster
@@ -2466,6 +2471,8 @@ def __init__(self, cluster, hosts, keyspace=None):
24662471
msg += " using keyspace '%s'" % self.keyspace
24672472
raise NoHostAvailable(msg, [h.address for h in hosts])
24682473

2474+
self._graph_paging_available = self._check_graph_paging_available()
2475+
24692476
cc_host = self.cluster.get_control_connection_host()
24702477
valid_insights_version = (cc_host and version_supports_insights(cc_host.dse_version))
24712478
if self.cluster.monitor_reporting_enabled and valid_insights_version:
@@ -2605,18 +2612,31 @@ def execute_graph_async(self, query, parameters=None, trace=False, execution_pro
26052612
if not isinstance(query, GraphStatement):
26062613
query = SimpleGraphStatement(query)
26072614

2608-
execution_profile = self._maybe_get_execution_profile(execution_profile) # look up instance here so we can apply the extended attributes
2615+
# Clone and look up instance here so we can resolve and apply the extended attributes
2616+
execution_profile = self.execution_profile_clone_update(execution_profile)
2617+
2618+
if not hasattr(execution_profile, 'graph_options'):
2619+
raise ValueError(
2620+
"Execution profile for graph queries must derive from GraphExecutionProfile, and provide graph_options")
2621+
2622+
self._resolve_execution_profile_options(execution_profile)
26092623

2624+
# make sure the graphson context row factory is binded to this cluster
26102625
try:
2611-
options = execution_profile.graph_options.copy()
2612-
except AttributeError:
2613-
raise ValueError("Execution profile for graph queries must derive from GraphExecutionProfile, and provide graph_options")
2626+
if issubclass(execution_profile.row_factory, _GraphSONContextRowFactory):
2627+
execution_profile.row_factory = execution_profile.row_factory(self.cluster)
2628+
except TypeError:
2629+
# issubclass might fail if arg1 is an instance
2630+
pass
2631+
2632+
# set graph paging if needed
2633+
self._maybe_set_graph_paging(execution_profile)
26142634

26152635
graph_parameters = None
26162636
if parameters:
2617-
graph_parameters = self._transform_params(parameters, graph_options=options)
2637+
graph_parameters = self._transform_params(parameters, graph_options=execution_profile.graph_options)
26182638

2619-
custom_payload = options.get_options_map()
2639+
custom_payload = execution_profile.graph_options.get_options_map()
26202640
if execute_as:
26212641
custom_payload[_proxy_execute_key] = six.b(execute_as)
26222642
custom_payload[_request_timeout_key] = int64_pack(long(execution_profile.request_timeout * 1000))
@@ -2627,25 +2647,98 @@ def execute_graph_async(self, query, parameters=None, trace=False, execution_pro
26272647
future.message.query_params = graph_parameters
26282648
future._protocol_handler = self.client_protocol_handler
26292649

2630-
if options.is_analytics_source and isinstance(execution_profile.load_balancing_policy, DefaultLoadBalancingPolicy):
2650+
if execution_profile.graph_options.is_analytics_source and \
2651+
isinstance(execution_profile.load_balancing_policy, DefaultLoadBalancingPolicy):
26312652
self._target_analytics_master(future)
26322653
else:
26332654
future.send_request()
26342655
return future
26352656

2657+
def _maybe_set_graph_paging(self, execution_profile):
2658+
graph_paging = execution_profile.continuous_paging_options
2659+
if execution_profile.continuous_paging_options is _NOT_SET:
2660+
graph_paging = ContinuousPagingOptions() if self._graph_paging_available else None
2661+
2662+
execution_profile.continuous_paging_options = graph_paging
2663+
2664+
def _check_graph_paging_available(self):
2665+
"""Verify if we can enable graph paging. This executed only once when the session is created."""
2666+
2667+
if not ProtocolVersion.has_continuous_paging_next_pages(self._protocol_version):
2668+
return False
2669+
2670+
for host in self.cluster.metadata.all_hosts():
2671+
if host.dse_version is None:
2672+
return False
2673+
2674+
version = Version(host.dse_version)
2675+
if version < _GRAPH_PAGING_MIN_DSE_VERSION:
2676+
return False
2677+
2678+
return True
2679+
2680+
def _resolve_execution_profile_options(self, execution_profile):
2681+
"""
2682+
Determine the GraphSON protocol and row factory for a graph query. This is useful
2683+
to configure automatically the execution profile when executing a query on a
2684+
core graph.
2685+
If `graph_protocol` is not explicitly specified, the following rules apply:
2686+
- Default to GraphProtocol.GRAPHSON_1_0, or GRAPHSON_2_0 if the `graph_language` is not gremlin-groovy.
2687+
- If `graph_options.graph_name` is specified and is a Core graph, set GraphSON_3_0.
2688+
If `row_factory` is not explicitly specified, the following rules apply:
2689+
- Default to graph_object_row_factory.
2690+
- If `graph_options.graph_name` is specified and is a Core graph, set graph_graphson3_row_factory.
2691+
"""
2692+
if execution_profile.graph_options.graph_protocol is not None and \
2693+
execution_profile.row_factory is not None:
2694+
return
2695+
2696+
graph_options = execution_profile.graph_options
2697+
2698+
is_core_graph = False
2699+
if graph_options.graph_name:
2700+
# graph_options.graph_name is bytes ...
2701+
name = graph_options.graph_name.decode('utf-8')
2702+
if name in self.cluster.metadata.keyspaces:
2703+
ks_metadata = self.cluster.metadata.keyspaces[name]
2704+
if ks_metadata.graph_engine == 'Core':
2705+
is_core_graph = True
2706+
2707+
if is_core_graph:
2708+
graph_protocol = GraphProtocol.GRAPHSON_3_0
2709+
row_factory = graph_graphson3_row_factory
2710+
else:
2711+
if graph_options.graph_language == GraphOptions.DEFAULT_GRAPH_LANGUAGE:
2712+
graph_protocol = GraphOptions.DEFAULT_GRAPH_PROTOCOL
2713+
row_factory = graph_object_row_factory
2714+
else:
2715+
# if not gremlin-groovy, GraphSON_2_0
2716+
graph_protocol = GraphProtocol.GRAPHSON_2_0
2717+
row_factory = graph_graphson2_row_factory
2718+
2719+
# Only apply if not set explicitly
2720+
if graph_options.graph_protocol is None:
2721+
graph_options.graph_protocol = graph_protocol
2722+
if execution_profile.row_factory is None:
2723+
execution_profile.row_factory = row_factory
2724+
26362725
def _transform_params(self, parameters, graph_options):
26372726
if not isinstance(parameters, dict):
26382727
raise ValueError('The parameters must be a dictionary. Unnamed parameters are not allowed.')
26392728

26402729
# Serialize python types to graphson
26412730
serializer = GraphSON1Serializer
26422731
if graph_options.graph_protocol == GraphProtocol.GRAPHSON_2_0:
2643-
serializer = GraphSON2Serializer
2644-
2645-
serialized_parameters = {
2646-
p: serializer.serialize(v)
2647-
for p, v in six.iteritems(parameters)
2648-
}
2732+
serializer = GraphSON2Serializer()
2733+
elif graph_options.graph_protocol == GraphProtocol.GRAPHSON_3_0:
2734+
# only required for core graphs
2735+
context = {
2736+
'cluster': self.cluster,
2737+
'graph_name': graph_options.graph_name.decode('utf-8') if graph_options.graph_name else None
2738+
}
2739+
serializer = GraphSON3Serializer(context)
2740+
2741+
serialized_parameters = serializer.serialize(parameters)
26492742
return [json.dumps(serialized_parameters).encode('utf-8')]
26502743

26512744
def _target_analytics_master(self, future):

cassandra/datastax/graph/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from cassandra.datastax.graph.query import (
1818
GraphOptions, GraphProtocol, GraphStatement, SimpleGraphStatement, Result,
1919
graph_object_row_factory, single_object_row_factory,
20-
graph_result_row_factory, graph_graphson2_row_factory
20+
graph_result_row_factory, graph_graphson2_row_factory,
21+
graph_graphson3_row_factory
2122
)
2223
from cassandra.datastax.graph.graphson import *

0 commit comments

Comments
 (0)