Skip to content

Commit 4f5040d

Browse files
committed
feat(CRC-1988): add QdrantCluster.spec.onDemandReplication field
It's an enum with Off/Auto/On. Off is the default. It'll be used by qdrant/operator and qdrant/node-operator. For existing Go clients `omitempty` is used on the field, avoiding a backwards incompatible change.
1 parent bbf4d05 commit 4f5040d

6 files changed

Lines changed: 102 additions & 6 deletions

File tree

api/v1/integration_test.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@ import (
77
. "github.com/onsi/gomega"
88
corev1 "k8s.io/api/core/v1"
99
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
1011
)
1112

1213
var _ = Describe("QdrantCluster", func() {
1314
Context("API integration tests", func() {
1415
const namespaceName = "test-namespace"
1516
ctx := context.Background()
16-
namespace := &corev1.Namespace{
17-
ObjectMeta: metav1.ObjectMeta{
18-
Name: namespaceName,
19-
},
20-
}
2117
BeforeEach(func() {
2218
By("Creating the Namespace to perform the tests")
19+
namespace := &corev1.Namespace{
20+
ObjectMeta: metav1.ObjectMeta{
21+
Name: namespaceName,
22+
},
23+
}
2324
err := k8sClient.Create(ctx, namespace)
24-
Expect(err).To(Not(HaveOccurred()))
25+
Expect(client.IgnoreAlreadyExists(err)).To(Not(HaveOccurred()))
2526
})
2627
It("should not flip ServicePerNode value on update", func() {
2728
qc := QdrantCluster{
@@ -44,6 +45,26 @@ var _ = Describe("QdrantCluster", func() {
4445
Expect(err).To(Not(HaveOccurred()))
4546
Expect(DerefPointer(qc.Spec.ServicePerNode)).To(BeFalse())
4647
})
48+
It("should default OnDemandReplication to Off when omitted", func() {
49+
qc := QdrantCluster{
50+
ObjectMeta: metav1.ObjectMeta{
51+
Namespace: namespaceName,
52+
Name: "test-cluster-on-demand-replication",
53+
},
54+
Spec: QdrantClusterSpec{
55+
Id: "test-cluster-on-demand-replication",
56+
Version: "v1.18.0",
57+
Size: 1,
58+
},
59+
}
60+
err := k8sClient.Create(ctx, &qc)
61+
Expect(err).To(Not(HaveOccurred()))
62+
63+
created := &QdrantCluster{}
64+
err = k8sClient.Get(ctx, client.ObjectKeyFromObject(&qc), created)
65+
Expect(err).To(Not(HaveOccurred()))
66+
Expect(created.Spec.OnDemandReplication).To(Equal(OnDemandReplicationOff))
67+
})
4768
})
4869

4970
})

api/v1/qdrantcluster_types.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ const (
2323
// If the value is updated it will retrigger the restart.
2424
// For historical reasons the key doesn't start with `operator.qdrant.com/`
2525
RestartedAtAnnotationKey = "restartedAt"
26+
// NodeUpdateRestartAnnotationKey is the annotation key to trigger a restart from the node-update-operator.
27+
// The annotation should be placed on the QdrantCluster instance.
28+
// The value should be a [RFC3339 formatted] date.
29+
// If the value is updated it will retrigger the restart.
30+
NodeUpdateRestartAnnotationKey = "operator.qdrant.com/node-update-restart"
2631
// RecreateNodeAnnotationKey is the annotation key to recreate a certain node.
2732
// The annotation should be placed on the pod created by the operator (for the node that need to be recreated).
2833
// It is allowed to add this annotation to multiple pods, the operator will handle them all.
@@ -58,6 +63,17 @@ const (
5863
ByCountAndSize RebalanceStrategy = "by_count_and_size"
5964
)
6065

66+
// OnDemandReplicationType specifies the on-demand replication restart mode.
67+
// +kubebuilder:validation:Enum=Off;Auto;On
68+
type OnDemandReplicationType string
69+
70+
//goland:noinspection GoUnusedConst
71+
const (
72+
OnDemandReplicationOff OnDemandReplicationType = "Off"
73+
OnDemandReplicationAuto OnDemandReplicationType = "Auto"
74+
OnDemandReplicationOn OnDemandReplicationType = "On"
75+
)
76+
6177
// QdrantClusterSpec defines the desired state of QdrantCluster
6278
// +kubebuilder:pruning:PreserveUnknownFields
6379
type QdrantClusterSpec struct {
@@ -136,6 +152,13 @@ type QdrantClusterSpec struct {
136152
// If unset, the operator is going to restart nodes concurrently if none of the collections if replicated.
137153
// +optional
138154
RestartAllPodsConcurrently *bool `json:"restartAllPodsConcurrently,omitempty"`
155+
// OnDemandReplication specifies the on-demand replication restart mode.
156+
// Off (default): Normal restart behavior. Pods are restarted directly.
157+
// Auto: The operator checks telemetry for non-replicated shards. If found, uses the recreate-node flow.
158+
// On: Always uses the recreate-node flow for eligible restart triggers.
159+
// +kubebuilder:default=Off
160+
// +optional
161+
OnDemandReplication OnDemandReplicationType `json:"onDemandReplication,omitempty"`
139162
// If StartupDelaySeconds is set (> 0), an additional 'sleep <value>' will be emitted to the pod startup.
140163
// The sleep will be added when a pod is restarted, it will not force any pod to restart.
141164
// This feature can be used for debugging the core, e.g. if a pod is in crash loop, it provided a way

api/v1/qdrantcluster_types_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
package v1
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
89
"k8s.io/utils/ptr"
910
)
1011

12+
func TestQdrantClusterSpecJSONOmitsUnsetOnDemandReplication(t *testing.T) {
13+
data, err := json.Marshal(QdrantClusterSpec{})
14+
15+
assert.NoError(t, err)
16+
assert.NotContains(t, string(data), "onDemandReplication")
17+
}
18+
1119
func TestValidate(t *testing.T) {
1220
testCases := []struct {
1321
name string

charts/qdrant-kubernetes-api/templates/region-crds/qdrant.io_qdrantclusters.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,18 @@ spec:
525525
description: NodeSelector specifies the node selector for each Qdrant
526526
node.
527527
type: object
528+
onDemandReplication:
529+
default: "Off"
530+
description: |-
531+
OnDemandReplication specifies the on-demand replication restart mode.
532+
Off (default): Normal restart behavior. Pods are restarted directly.
533+
Auto: The operator checks telemetry for non-replicated shards. If found, uses the recreate-node flow.
534+
On: Always uses the recreate-node flow for eligible restart triggers.
535+
enum:
536+
- "Off"
537+
- Auto
538+
- "On"
539+
type: string
528540
pauses:
529541
description: |-
530542
Pauses specifies a list of pause request by developer for manual maintenance.

crds/qdrant.io_qdrantclusters.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,18 @@ spec:
524524
description: NodeSelector specifies the node selector for each Qdrant
525525
node.
526526
type: object
527+
onDemandReplication:
528+
default: "Off"
529+
description: |-
530+
OnDemandReplication specifies the on-demand replication restart mode.
531+
Off (default): Normal restart behavior. Pods are restarted directly.
532+
Auto: The operator checks telemetry for non-replicated shards. If found, uses the recreate-node flow.
533+
On: Always uses the recreate-node flow for eligible restart triggers.
534+
enum:
535+
- "Off"
536+
- Auto
537+
- "On"
538+
type: string
527539
pauses:
528540
description: |-
529541
Pauses specifies a list of pause request by developer for manual maintenance.

docs/api.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,25 @@ _Appears in:_
620620
| `snapshotsPVCStatus` _[NodePVCStatus](#nodepvcstatus)_ | Status of the snapshots storage PVC | | Optional: \{\} <br /> |
621621

622622

623+
#### OnDemandReplicationType
624+
625+
_Underlying type:_ _string_
626+
627+
OnDemandReplicationType specifies the on-demand replication restart mode.
628+
629+
_Validation:_
630+
- Enum: [Off Auto On]
631+
632+
_Appears in:_
633+
- [QdrantClusterSpec](#qdrantclusterspec)
634+
635+
| Field | Description |
636+
| --- | --- |
637+
| `Off` | |
638+
| `Auto` | |
639+
| `On` | |
640+
641+
623642
#### Pause
624643

625644

@@ -976,6 +995,7 @@ _Appears in:_
976995
| `topologySpreadConstraints` _[TopologySpreadConstraint](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#topologyspreadconstraint-v1-core)_ | TopologySpreadConstraints specifies the topology spread constraints for the cluster. | | Optional: \{\} <br /> |
977996
| `podDisruptionBudget` _[PodDisruptionBudgetSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#poddisruptionbudgetspec-v1-policy)_ | PodDisruptionBudget specifies the pod disruption budget for the cluster. | | Optional: \{\} <br /> |
978997
| `restartAllPodsConcurrently` _boolean_ | RestartAllPodsConcurrently specifies whether to restart all pods concurrently (also called one-shot-restart).<br />If enabled, all the pods in the cluster will be restarted concurrently in situations where multiple pods<br />need to be restarted, like when RestartedAtAnnotationKey is added/updated or the Qdrant version needs to be upgraded.<br />This helps sharded but not replicated clusters to reduce downtime to a possible minimum during restart.<br />If unset, the operator is going to restart nodes concurrently if none of the collections if replicated. | | Optional: \{\} <br /> |
998+
| `onDemandReplication` _[OnDemandReplicationType](#ondemandreplicationtype)_ | OnDemandReplication specifies the on-demand replication restart mode.<br />Off (default): Normal restart behavior. Pods are restarted directly.<br />Auto: The operator checks telemetry for non-replicated shards. If found, uses the recreate-node flow.<br />On: Always uses the recreate-node flow for eligible restart triggers. | Off | Enum: [Off Auto On] <br />Optional: \{\} <br /> |
979999
| `startupDelaySeconds` _integer_ | If StartupDelaySeconds is set (> 0), an additional 'sleep <value>' will be emitted to the pod startup.<br />The sleep will be added when a pod is restarted, it will not force any pod to restart.<br />This feature can be used for debugging the core, e.g. if a pod is in crash loop, it provided a way<br />to inspect the attached storage. | | Optional: \{\} <br /> |
9801000
| `rebalanceStrategy` _[RebalanceStrategy](#rebalancestrategy)_ | RebalanceStrategy specifies the strategy to use for automaticially rebalancing shards the cluster.<br />Cluster-manager needs to be enabled for this feature to work. | | Enum: [by_count by_size by_count_and_size] <br />Optional: \{\} <br /> |
9811001
| `readClusters` _[ReadCluster](#readcluster) array_ | ReadClusters specifies the read clusters for this cluster to synchronize.<br />Cluster-manager needs to be enabled for this feature to work. | | Optional: \{\} <br /> |

0 commit comments

Comments
 (0)