TestLossyIndexInUncommittedTxn tests that queries within an uncommitted transaction can find data that was mutated in that same transaction when using a lossy index like @index(hour). Issue #9556: The bug occurs because lossy indexes require a two-step query: 1. Index lookup - finds candidate UIDs
(t *testing.T)
| 2897 | // 1. Index lookup - finds candidate UIDs |
| 2898 | // 2. Value verification - re-checks actual values since hour granularity is imprecise |
| 2899 | func TestLossyIndexInUncommittedTxn(t *testing.T) { |
| 2900 | ctx := context.Background() |
| 2901 | |
| 2902 | // Use a unique datetime value to avoid conflicts with existing test data |
| 2903 | testTime := time.Now().UTC().Truncate(time.Second) |
| 2904 | testTimeStr := testTime.Format(time.RFC3339) |
| 2905 | |
| 2906 | // Create a new transaction - DO NOT commit yet |
| 2907 | txn := client.NewTxn() |
| 2908 | defer func() { |
| 2909 | if err := txn.Discard(ctx); err != nil { |
| 2910 | t.Logf("error discarding txn: %v", err) |
| 2911 | } |
| 2912 | }() |
| 2913 | |
| 2914 | mutationJSON := fmt.Sprintf(`{ |
| 2915 | "uid": "_:newnode", |
| 2916 | "dgraph.type": "TestNode", |
| 2917 | "created_at": "%s" |
| 2918 | }`, testTimeStr) |
| 2919 | |
| 2920 | resp, err := txn.Mutate(ctx, &api.Mutation{ |
| 2921 | SetJson: []byte(mutationJSON), |
| 2922 | }) |
| 2923 | require.NoError(t, err, "mutation should succeed") |
| 2924 | require.NotEmpty(t, resp.Uids["newnode"], "should get a UID for the new node") |
| 2925 | |
| 2926 | newUID := resp.Uids["newnode"] |
| 2927 | t.Logf("Created node with UID %s and created_at=%s", newUID, testTimeStr) |
| 2928 | |
| 2929 | // Query for the same data within the SAME uncommitted transaction |
| 2930 | // This query uses the lossy @index(hour) on created_at |
| 2931 | query := fmt.Sprintf(`{ |
| 2932 | q(func: eq(created_at, "%s")) { |
| 2933 | uid |
| 2934 | created_at |
| 2935 | } |
| 2936 | }`, testTimeStr) |
| 2937 | |
| 2938 | queryResp, err := txn.Query(ctx, query) |
| 2939 | require.NoError(t, err, "query should succeed") |
| 2940 | |
| 2941 | var result struct { |
| 2942 | Q []struct { |
| 2943 | UID string `json:"uid"` |
| 2944 | CreatedAt string `json:"created_at"` |
| 2945 | } `json:"q"` |
| 2946 | } |
| 2947 | err = json.Unmarshal(queryResp.Json, &result) |
| 2948 | require.NoError(t, err, "should be able to parse response") |
| 2949 | |
| 2950 | t.Logf("Query response: %s", string(queryResp.Json)) |
| 2951 | |
| 2952 | require.Len(t, result.Q, 1, "should find exactly 1 node with the matching created_at") |
| 2953 | require.Equal(t, newUID, result.Q[0].UID, "should find the node we just created") |
| 2954 | } |
| 2955 | |
| 2956 | func TestCountUidWithAlias(t *testing.T) { |